Memory controller drivers for v6.12

1. Tegra210 EMC: Driver refactoring and rework.
 2. Tegra186 EMC: Drop unused function.
 3. FSL WEIM: Correct fsl,weim-cs-timing property to properly validate it
    as an array.
 4. TI AEMIF: Drop platform data support.
 5. TI EMIF: Switch to of_property_read_bool().
 
 6. Several cleanups in multiple drivers: TI AEMIF and EMIF, Tegra
    EMC/MC, Atmel EBI, Samsung Exynos5422 DMC, STM32 FMC2 EBI, OMAP GPMC,
    PL172 and PL1353 SMC.  These are mostly code simplifying around
    probe() like using
     - devm_clk_get_enabled(),
     - dev_err_probe(),
     - scoped device node handling (cleanup.h),
     - scoped for each OF child loops,
     - scoped/guard locks.
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEE3dJiKD0RGyM7briowTdm5oaLg9cFAmbNxnEQHGtyemtAa2Vy
 bmVsLm9yZwAKCRDBN2bmhouD14y6D/4j64Gb8oH5YimxYQz/97B9tTsI0wzWoiPX
 P+pUY1lWDGuN5CwD7LZrBtK2DKk6QJQcQTu7tav5iV0gt1eMYwzHLz+hBuL1TmHH
 SFqApBh1qxib8dyTuu/IFnwqXPJ9vfRwYqWEnUQ66mDe54qlhnlDVgty5ls9LFlA
 cSF4VU+ZctZsUlIy1zAQYq/GLaKh7JA4nn8XkbcoVaDVJ7j+dikovMFE6Thcgcn1
 55nukbaArxjJk/3bwoUNccNy6hiazxTvBjGkFuS5YhWAcLyJfkBGOPpx0vxMSjl0
 n5R+cN/r6dLnNnRXhD6H8ztlq5xWcO1ziUwIOtHCPGgyOcDZ93cKGcHaUiwd5ZlT
 /afYHQSQupty4ntpqK7OasyXyfniprXC/KUbpiodmi63Q1YGdEJYp6Q42g/1Xj73
 XKch3aOshFePdb2QAZuWXISTucu3cQw7iL7j/FJp6spLSR5W/30lEIUwjAcVrPrF
 V4S2WiwQxqZZQqFtmijnoC76bdLS76MqlRkZlFx0wpM6ILnQgjqLGmENYaMm8rqV
 myIrc/cQ3LJX0+kKisVdfbHNs1KWVh3s6GCDrvLlUnqRcjrzvTxV1uwkJVcN3U2g
 OOpsbJYujYzpw4JMdsFK481HE3+gOhN/jYIc0vUooXw3LkKtx4Rk7cPnAmDfju7C
 AKhEikLr+g==
 =/0oc
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmbViXcACgkQYKtH/8kJ
 UifDtw//VhTtafBj9zQgOCUcdUhYVEbVI/CvHsmtFI87b+ovC0JyzJQL/TP0bTcH
 xrk3pN1/+T1A0hTPR59b93uJVNd082iz0LA+1L5Um6bMf4wziLF8MwPg7oORNVCh
 dM2B/5UYEdz74lJDda2FThHQ0LBP3to+GzERMOWUN4kQApHiM63q4uU1Lz5O/v/X
 ULCe6mwAqn8ugW0LmPUiZwXPxYraK/MQniWlK+IfxJuYeRrc4dciSEAwvjt3yG0Q
 2yDuIRn2Va6+q0uE9N/gY3Yc2t1idBpnaD/nxFO6jmTk3a+x4zI6fIbvLLvzWBbb
 aVQlqWoNsvSJ7msNLIZH0O7J2vszRbphiTDRTkT9sHqNnJ4A4szd7C+b5/g3EGsZ
 LOSVsvEMeVHmNAL9CJEbgA2UtdoPJL5mPIWLaC2XIR6h0eWd3+p56bg4msToLsj/
 gkIJkwXjltxjQJ6NfCHfJC3BbbtHYnNeA+xWGbCVVaCEjEu88rnWlRpOKhEKc2cH
 tWYF13sSGmZqVNDXarItrLL6xCOD30l+QkjO3l8R/gphqfNAS8dXBviDxjxMiwp5
 vjA7cUROgu9I5P6sCYFFohBmYWTcH1/Kl4Gks0JAAD48gd8e8LaZP8pyuVP21dEh
 9f7e1FhsDKCQYZMq/WCJZR1fGGvD2OZYi+qfo8aTUDbuH9fCK4Q=
 =W0Pq
 -----END PGP SIGNATURE-----

Merge tag 'memory-controller-drv-6.12' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into soc/drivers

Memory controller drivers for v6.12

1. Tegra210 EMC: Driver refactoring and rework.
2. Tegra186 EMC: Drop unused function.
3. FSL WEIM: Correct fsl,weim-cs-timing property to properly validate it
   as an array.
4. TI AEMIF: Drop platform data support.
5. TI EMIF: Switch to of_property_read_bool().

6. Several cleanups in multiple drivers: TI AEMIF and EMIF, Tegra
   EMC/MC, Atmel EBI, Samsung Exynos5422 DMC, STM32 FMC2 EBI, OMAP GPMC,
   PL172 and PL1353 SMC.  These are mostly code simplifying around
   probe() like using
    - devm_clk_get_enabled(),
    - dev_err_probe(),
    - scoped device node handling (cleanup.h),
    - scoped for each OF child loops,
    - scoped/guard locks.

* tag 'memory-controller-drv-6.12' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl: (35 commits)
  memory: mtk-smi: Use devm_clk_get_enabled()
  memory: pl353-smc: simplify with devm_clk_get_enabled()
  memory: pl353-smc: simplify with dev_err_probe()
  memory: pl172: simplify with devm_clk_get_enabled()
  memory: pl172: simplify with dev_err_probe()
  memory: omap-gpmc: simplify locking with guard()
  memory: emif: simplify locking with guard()
  memory: emif: drop unused 'irq_state' member
  memory: ti-aemif: Revert "memory: ti-aemif: don't needlessly iterate over child nodes"
  memory: ti-aemif: simplify with scoped for each OF child loop
  memory: ti-aemif: simplify with dev_err_probe()
  memory: tegra30-emc: simplify with scoped for each OF child loop
  memory: tegra20-emc: simplify with scoped for each OF child loop
  memory: tegra124-emc: simplify with scoped for each OF child loop
  memory: tegra-mc: simplify with scoped for each OF child loop
  memory: stm32-fmc2-ebi: simplify with dev_err_probe()
  memory: stm32-fmc2-ebi: simplify with scoped for each OF child loop
  memory: samsung: exynos5422-dmc: use scoped device node handling to simplify error paths
  memory: samsung: exynos5422-dmc: simplify dmc->dev usage
  memory: atmel-ebi: simplify with scoped for each OF child loop
  ...

Link: https://lore.kernel.org/r/20240827122926.30794-1-krzysztof.kozlowski@linaro.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2024-09-02 09:45:17 +00:00
commit 3a059ee655
18 changed files with 236 additions and 693 deletions

View file

@ -134,9 +134,8 @@ allOf:
properties:
fsl,weim-cs-timing:
items:
items:
- description: CSxU
- description: CSxL
- description: CSxU
- description: CSxL
- if:
properties:
compatible:
@ -151,10 +150,9 @@ allOf:
properties:
fsl,weim-cs-timing:
items:
items:
- description: CSCRxU
- description: CSCRxL
- description: CSCRxA
- description: CSCRxU
- description: CSCRxL
- description: CSCRxA
- if:
properties:
compatible:
@ -171,13 +169,12 @@ allOf:
properties:
fsl,weim-cs-timing:
items:
items:
- description: CSxGCR1
- description: CSxGCR2
- description: CSxRCR1
- description: CSxRCR2
- description: CSxWCR1
- description: CSxWCR2
- description: CSxGCR1
- description: CSxGCR2
- description: CSxRCR1
- description: CSxRCR2
- description: CSxWCR1
- description: CSxWCR2
additionalProperties: false

View file

@ -67,7 +67,9 @@ properties:
- const: dirmap
- const: wbuf
clocks: true
clocks:
minItems: 1
maxItems: 2
interrupts:
maxItems: 1

View file

@ -6,6 +6,7 @@
* Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com>
*/
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
@ -517,7 +518,7 @@ static int atmel_ebi_dev_disable(struct atmel_ebi *ebi, struct device_node *np)
static int atmel_ebi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node, *smc_np;
struct device_node *np = dev->of_node;
struct atmel_ebi *ebi;
int ret, reg_cells;
struct clk *clk;
@ -541,30 +542,24 @@ static int atmel_ebi_probe(struct platform_device *pdev)
ebi->clk = clk;
smc_np = of_parse_phandle(dev->of_node, "atmel,smc", 0);
struct device_node *smc_np __free(device_node) =
of_parse_phandle(dev->of_node, "atmel,smc", 0);
ebi->smc.regmap = syscon_node_to_regmap(smc_np);
if (IS_ERR(ebi->smc.regmap)) {
ret = PTR_ERR(ebi->smc.regmap);
goto put_node;
}
if (IS_ERR(ebi->smc.regmap))
return PTR_ERR(ebi->smc.regmap);
ebi->smc.layout = atmel_hsmc_get_reg_layout(smc_np);
if (IS_ERR(ebi->smc.layout)) {
ret = PTR_ERR(ebi->smc.layout);
goto put_node;
}
if (IS_ERR(ebi->smc.layout))
return PTR_ERR(ebi->smc.layout);
ebi->smc.clk = of_clk_get(smc_np, 0);
if (IS_ERR(ebi->smc.clk)) {
if (PTR_ERR(ebi->smc.clk) != -ENOENT) {
ret = PTR_ERR(ebi->smc.clk);
goto put_node;
}
if (PTR_ERR(ebi->smc.clk) != -ENOENT)
return PTR_ERR(ebi->smc.clk);
ebi->smc.clk = NULL;
}
of_node_put(smc_np);
ret = clk_prepare_enable(ebi->smc.clk);
if (ret)
return ret;
@ -597,7 +592,7 @@ static int atmel_ebi_probe(struct platform_device *pdev)
reg_cells += val;
for_each_available_child_of_node(np, child) {
for_each_available_child_of_node_scoped(np, child) {
if (!of_property_present(child, "reg"))
continue;
@ -607,18 +602,12 @@ static int atmel_ebi_probe(struct platform_device *pdev)
child);
ret = atmel_ebi_dev_disable(ebi, child);
if (ret) {
of_node_put(child);
if (ret)
return ret;
}
}
}
return of_platform_populate(np, NULL, NULL, dev);
put_node:
of_node_put(smc_np);
return ret;
}
static __maybe_unused int atmel_ebi_resume(struct device *dev)

View file

@ -7,6 +7,7 @@
* Aneesh V <aneesh@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
*/
#include <linux/cleanup.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
@ -57,7 +58,6 @@ struct emif_data {
u8 temperature_level;
u8 lpmode;
struct list_head node;
unsigned long irq_state;
void __iomem *base;
struct device *dev;
struct emif_regs *regs_cache[EMIF_MAX_NUM_FREQUENCIES];
@ -69,7 +69,6 @@ struct emif_data {
static struct emif_data *emif1;
static DEFINE_SPINLOCK(emif_lock);
static unsigned long irq_state;
static LIST_HEAD(device_list);
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
@ -523,18 +522,18 @@ static void setup_temperature_sensitive_regs(struct emif_data *emif,
static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
{
u32 old_temp_level;
irqreturn_t ret = IRQ_HANDLED;
irqreturn_t ret;
struct emif_custom_configs *custom_configs;
spin_lock_irqsave(&emif_lock, irq_state);
guard(spinlock_irqsave)(&emif_lock);
old_temp_level = emif->temperature_level;
get_temperature_level(emif);
if (unlikely(emif->temperature_level == old_temp_level)) {
goto out;
return IRQ_HANDLED;
} else if (!emif->curr_regs) {
dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n");
goto out;
return IRQ_HANDLED;
}
custom_configs = emif->plat_data->custom_configs;
@ -554,8 +553,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
* from thread context
*/
emif->temperature_level = SDRAM_TEMP_VERY_HIGH_SHUTDOWN;
ret = IRQ_WAKE_THREAD;
goto out;
return IRQ_WAKE_THREAD;
}
}
@ -571,10 +569,9 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
/* Temperature is going up - handle immediately */
setup_temperature_sensitive_regs(emif, emif->curr_regs);
do_freq_update();
ret = IRQ_HANDLED;
}
out:
spin_unlock_irqrestore(&emif_lock, irq_state);
return ret;
}
@ -617,6 +614,7 @@ static irqreturn_t emif_interrupt_handler(int irq, void *dev_id)
static irqreturn_t emif_threaded_isr(int irq, void *dev_id)
{
struct emif_data *emif = dev_id;
unsigned long irq_state;
if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
@ -864,7 +862,7 @@ static void of_get_custom_configs(struct device_node *np_emif,
be32_to_cpup(poll_intvl);
}
if (of_find_property(np_emif, "extended-temp-part", &len))
if (of_property_read_bool(np_emif, "extended-temp-part"))
cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART;
if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
@ -880,13 +878,9 @@ static void of_get_ddr_info(struct device_node *np_emif,
struct ddr_device_info *dev_info)
{
u32 density = 0, io_width = 0;
int len;
if (of_find_property(np_emif, "cs1-used", &len))
dev_info->cs1_used = true;
if (of_find_property(np_emif, "cal-resistor-per-cs", &len))
dev_info->cal_resistors_per_cs = true;
dev_info->cs1_used = of_property_read_bool(np_emif, "cs1-used");
dev_info->cal_resistors_per_cs = of_property_read_bool(np_emif, "cal-resistor-per-cs");
if (of_device_is_compatible(np_ddr, "jedec,lpddr2-s4"))
dev_info->type = DDR_TYPE_LPDDR2_S4;
@ -916,7 +910,6 @@ static struct emif_data *of_get_memory_device_details(
struct ddr_device_info *dev_info = NULL;
struct emif_platform_data *pd = NULL;
struct device_node *np_ddr;
int len;
np_ddr = of_parse_phandle(np_emif, "device-handle", 0);
if (!np_ddr)
@ -944,7 +937,7 @@ static struct emif_data *of_get_memory_device_details(
of_property_read_u32(np_emif, "phy-type", &pd->phy_type);
if (of_find_property(np_emif, "hw-caps-ll-interface", &len))
if (of_property_read_bool(np_emif, "hw-caps-ll-interface"))
pd->hw_caps |= EMIF_HW_CAPS_LL_INTERFACE;
of_get_ddr_info(np_emif, np_ddr, dev_info);

View file

@ -771,13 +771,9 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
if (IS_ERR(common->smi_ao_base))
return PTR_ERR(common->smi_ao_base);
common->clk_async = devm_clk_get(dev, "async");
common->clk_async = devm_clk_get_enabled(dev, "async");
if (IS_ERR(common->clk_async))
return PTR_ERR(common->clk_async);
ret = clk_prepare_enable(common->clk_async);
if (ret)
return ret;
} else {
common->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(common->base))

View file

@ -9,6 +9,7 @@
* Copyright (C) 2009 Texas Instruments
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
*/
#include <linux/cleanup.h>
#include <linux/cpu_pm.h>
#include <linux/irq.h>
#include <linux/kernel.h>
@ -989,18 +990,18 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
if (size > (1 << GPMC_SECTION_SHIFT))
return -ENOMEM;
spin_lock(&gpmc_mem_lock);
if (gpmc_cs_reserved(cs)) {
r = -EBUSY;
goto out;
}
guard(spinlock)(&gpmc_mem_lock);
if (gpmc_cs_reserved(cs))
return -EBUSY;
if (gpmc_cs_mem_enabled(cs))
r = adjust_resource(res, res->start & ~(size - 1), size);
if (r < 0)
r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0,
size, NULL, NULL);
if (r < 0)
goto out;
return r;
/* Disable CS while changing base address and size mask */
gpmc_cs_disable_mem(cs);
@ -1008,16 +1009,15 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
r = gpmc_cs_set_memconf(cs, res->start, resource_size(res));
if (r < 0) {
release_resource(res);
goto out;
return r;
}
/* Enable CS */
gpmc_cs_enable_mem(cs);
*base = res->start;
gpmc_cs_set_reserved(cs, 1);
out:
spin_unlock(&gpmc_mem_lock);
return r;
return 0;
}
EXPORT_SYMBOL(gpmc_cs_request);
@ -1026,10 +1026,9 @@ void gpmc_cs_free(int cs)
struct gpmc_cs_data *gpmc;
struct resource *res;
spin_lock(&gpmc_mem_lock);
guard(spinlock)(&gpmc_mem_lock);
if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {
WARN(1, "Trying to free non-reserved GPMC CS%d\n", cs);
spin_unlock(&gpmc_mem_lock);
return;
}
gpmc = &gpmc_cs[cs];
@ -1039,7 +1038,6 @@ void gpmc_cs_free(int cs)
if (res->flags)
release_resource(res);
gpmc_cs_set_reserved(cs, 0);
spin_unlock(&gpmc_mem_lock);
}
EXPORT_SYMBOL(gpmc_cs_free);

View file

@ -216,29 +216,20 @@ static int pl172_probe(struct amba_device *adev, const struct amba_id *id)
if (!pl172)
return -ENOMEM;
pl172->clk = devm_clk_get(dev, "mpmcclk");
if (IS_ERR(pl172->clk)) {
dev_err(dev, "no mpmcclk provided clock\n");
return PTR_ERR(pl172->clk);
}
ret = clk_prepare_enable(pl172->clk);
if (ret) {
dev_err(dev, "unable to mpmcclk enable clock\n");
return ret;
}
pl172->clk = devm_clk_get_enabled(dev, "mpmcclk");
if (IS_ERR(pl172->clk))
return dev_err_probe(dev, PTR_ERR(pl172->clk),
"no mpmcclk provided clock\n");
pl172->rate = clk_get_rate(pl172->clk) / MSEC_PER_SEC;
if (!pl172->rate) {
dev_err(dev, "unable to get mpmcclk clock rate\n");
ret = -EINVAL;
goto err_clk_enable;
}
if (!pl172->rate)
return dev_err_probe(dev, -EINVAL,
"unable to get mpmcclk clock rate\n");
ret = amba_request_regions(adev, NULL);
if (ret) {
dev_err(dev, "unable to request AMBA regions\n");
goto err_clk_enable;
return ret;
}
pl172->base = devm_ioremap(dev, adev->res.start,
@ -268,16 +259,11 @@ static int pl172_probe(struct amba_device *adev, const struct amba_id *id)
err_no_ioremap:
amba_release_regions(adev);
err_clk_enable:
clk_disable_unprepare(pl172->clk);
return ret;
}
static void pl172_remove(struct amba_device *adev)
{
struct pl172_data *pl172 = amba_get_drvdata(adev);
clk_disable_unprepare(pl172->clk);
amba_release_regions(adev);
}

View file

@ -75,35 +75,20 @@ static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
const struct of_device_id *match = NULL;
struct pl353_smc_data *pl353_smc;
struct device_node *child;
int err;
pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL);
if (!pl353_smc)
return -ENOMEM;
pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk");
if (IS_ERR(pl353_smc->aclk)) {
dev_err(&adev->dev, "aclk clock not found.\n");
return PTR_ERR(pl353_smc->aclk);
}
pl353_smc->aclk = devm_clk_get_enabled(&adev->dev, "apb_pclk");
if (IS_ERR(pl353_smc->aclk))
return dev_err_probe(&adev->dev, PTR_ERR(pl353_smc->aclk),
"aclk clock not found.\n");
pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk");
if (IS_ERR(pl353_smc->memclk)) {
dev_err(&adev->dev, "memclk clock not found.\n");
return PTR_ERR(pl353_smc->memclk);
}
err = clk_prepare_enable(pl353_smc->aclk);
if (err) {
dev_err(&adev->dev, "Unable to enable AXI clock.\n");
return err;
}
err = clk_prepare_enable(pl353_smc->memclk);
if (err) {
dev_err(&adev->dev, "Unable to enable memory clock.\n");
goto disable_axi_clk;
}
pl353_smc->memclk = devm_clk_get_enabled(&adev->dev, "memclk");
if (IS_ERR(pl353_smc->memclk))
return dev_err_probe(&adev->dev, PTR_ERR(pl353_smc->memclk),
"memclk clock not found.\n");
amba_set_drvdata(adev, pl353_smc);
@ -117,30 +102,14 @@ static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
break;
}
if (!match) {
err = -ENODEV;
dev_err(&adev->dev, "no matching children\n");
goto disable_mem_clk;
return -ENODEV;
}
of_platform_device_create(child, NULL, &adev->dev);
of_node_put(child);
return 0;
disable_mem_clk:
clk_disable_unprepare(pl353_smc->memclk);
disable_axi_clk:
clk_disable_unprepare(pl353_smc->aclk);
return err;
}
static void pl353_smc_remove(struct amba_device *adev)
{
struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev);
clk_disable_unprepare(pl353_smc->memclk);
clk_disable_unprepare(pl353_smc->aclk);
}
static const struct amba_id pl353_ids[] = {
@ -159,7 +128,6 @@ static struct amba_driver pl353_smc_driver = {
},
.id_table = pl353_ids,
.probe = pl353_smc_probe,
.remove = pl353_smc_remove,
};
module_amba_driver(pl353_smc_driver);

View file

@ -4,6 +4,7 @@
* Author: Lukasz Luba <l.luba@partner.samsung.com>
*/
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/devfreq-event.h>
@ -339,19 +340,20 @@ static int exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set)
static int exynos5_init_freq_table(struct exynos5_dmc *dmc,
struct devfreq_dev_profile *profile)
{
struct device *dev = dmc->dev;
int i, ret;
int idx;
unsigned long freq;
ret = devm_pm_opp_of_add_table(dmc->dev);
ret = devm_pm_opp_of_add_table(dev);
if (ret < 0) {
dev_err(dmc->dev, "Failed to get OPP table\n");
dev_err(dev, "Failed to get OPP table\n");
return ret;
}
dmc->opp_count = dev_pm_opp_get_opp_count(dmc->dev);
dmc->opp_count = dev_pm_opp_get_opp_count(dev);
dmc->opp = devm_kmalloc_array(dmc->dev, dmc->opp_count,
dmc->opp = devm_kmalloc_array(dev, dmc->opp_count,
sizeof(struct dmc_opp_table), GFP_KERNEL);
if (!dmc->opp)
return -ENOMEM;
@ -360,7 +362,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc,
for (i = 0, freq = ULONG_MAX; i < dmc->opp_count; i++, freq--) {
struct dev_pm_opp *opp;
opp = dev_pm_opp_find_freq_floor(dmc->dev, &freq);
opp = dev_pm_opp_find_freq_floor(dev, &freq);
if (IS_ERR(opp))
return PTR_ERR(opp);
@ -1175,51 +1177,44 @@ static int create_timings_aligned(struct exynos5_dmc *dmc, u32 *reg_timing_row,
static int of_get_dram_timings(struct exynos5_dmc *dmc)
{
int ret = 0;
struct device *dev = dmc->dev;
int idx;
struct device_node *np_ddr;
u32 freq_mhz, clk_period_ps;
np_ddr = of_parse_phandle(dmc->dev->of_node, "device-handle", 0);
struct device_node *np_ddr __free(device_node) =
of_parse_phandle(dev->of_node, "device-handle", 0);
if (!np_ddr) {
dev_warn(dmc->dev, "could not find 'device-handle' in DT\n");
dev_warn(dev, "could not find 'device-handle' in DT\n");
return -EINVAL;
}
dmc->timing_row = devm_kmalloc_array(dmc->dev, TIMING_COUNT,
dmc->timing_row = devm_kmalloc_array(dev, TIMING_COUNT,
sizeof(u32), GFP_KERNEL);
if (!dmc->timing_row) {
ret = -ENOMEM;
goto put_node;
}
if (!dmc->timing_row)
return -ENOMEM;
dmc->timing_data = devm_kmalloc_array(dmc->dev, TIMING_COUNT,
dmc->timing_data = devm_kmalloc_array(dev, TIMING_COUNT,
sizeof(u32), GFP_KERNEL);
if (!dmc->timing_data) {
ret = -ENOMEM;
goto put_node;
}
if (!dmc->timing_data)
return -ENOMEM;
dmc->timing_power = devm_kmalloc_array(dmc->dev, TIMING_COUNT,
dmc->timing_power = devm_kmalloc_array(dev, TIMING_COUNT,
sizeof(u32), GFP_KERNEL);
if (!dmc->timing_power) {
ret = -ENOMEM;
goto put_node;
}
if (!dmc->timing_power)
return -ENOMEM;
dmc->timings = of_lpddr3_get_ddr_timings(np_ddr, dmc->dev,
dmc->timings = of_lpddr3_get_ddr_timings(np_ddr, dev,
DDR_TYPE_LPDDR3,
&dmc->timings_arr_size);
if (!dmc->timings) {
dev_warn(dmc->dev, "could not get timings from DT\n");
ret = -EINVAL;
goto put_node;
dev_warn(dev, "could not get timings from DT\n");
return -EINVAL;
}
dmc->min_tck = of_lpddr3_get_min_tck(np_ddr, dmc->dev);
dmc->min_tck = of_lpddr3_get_min_tck(np_ddr, dev);
if (!dmc->min_tck) {
dev_warn(dmc->dev, "could not get tck from DT\n");
ret = -EINVAL;
goto put_node;
dev_warn(dev, "could not get tck from DT\n");
return -EINVAL;
}
/* Sorted array of OPPs with frequency ascending */
@ -1239,8 +1234,6 @@ static int of_get_dram_timings(struct exynos5_dmc *dmc)
dmc->bypass_timing_data = dmc->timing_data[idx - 1];
dmc->bypass_timing_power = dmc->timing_power[idx - 1];
put_node:
of_node_put(np_ddr);
return ret;
}
@ -1254,34 +1247,34 @@ static int of_get_dram_timings(struct exynos5_dmc *dmc)
static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc)
{
int ret;
struct device *dev = dmc->dev;
unsigned long target_volt = 0;
unsigned long target_rate = 0;
unsigned int tmp;
dmc->fout_spll = devm_clk_get(dmc->dev, "fout_spll");
dmc->fout_spll = devm_clk_get(dev, "fout_spll");
if (IS_ERR(dmc->fout_spll))
return PTR_ERR(dmc->fout_spll);
dmc->fout_bpll = devm_clk_get(dmc->dev, "fout_bpll");
dmc->fout_bpll = devm_clk_get(dev, "fout_bpll");
if (IS_ERR(dmc->fout_bpll))
return PTR_ERR(dmc->fout_bpll);
dmc->mout_mclk_cdrex = devm_clk_get(dmc->dev, "mout_mclk_cdrex");
dmc->mout_mclk_cdrex = devm_clk_get(dev, "mout_mclk_cdrex");
if (IS_ERR(dmc->mout_mclk_cdrex))
return PTR_ERR(dmc->mout_mclk_cdrex);
dmc->mout_bpll = devm_clk_get(dmc->dev, "mout_bpll");
dmc->mout_bpll = devm_clk_get(dev, "mout_bpll");
if (IS_ERR(dmc->mout_bpll))
return PTR_ERR(dmc->mout_bpll);
dmc->mout_mx_mspll_ccore = devm_clk_get(dmc->dev,
"mout_mx_mspll_ccore");
dmc->mout_mx_mspll_ccore = devm_clk_get(dev, "mout_mx_mspll_ccore");
if (IS_ERR(dmc->mout_mx_mspll_ccore))
return PTR_ERR(dmc->mout_mx_mspll_ccore);
dmc->mout_spll = devm_clk_get(dmc->dev, "ff_dout_spll2");
dmc->mout_spll = devm_clk_get(dev, "ff_dout_spll2");
if (IS_ERR(dmc->mout_spll)) {
dmc->mout_spll = devm_clk_get(dmc->dev, "mout_sclk_spll");
dmc->mout_spll = devm_clk_get(dev, "mout_sclk_spll");
if (IS_ERR(dmc->mout_spll))
return PTR_ERR(dmc->mout_spll);
}
@ -1329,38 +1322,37 @@ static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc)
*/
static int exynos5_performance_counters_init(struct exynos5_dmc *dmc)
{
struct device *dev = dmc->dev;
int ret, i;
dmc->num_counters = devfreq_event_get_edev_count(dmc->dev,
"devfreq-events");
dmc->num_counters = devfreq_event_get_edev_count(dev, "devfreq-events");
if (dmc->num_counters < 0) {
dev_err(dmc->dev, "could not get devfreq-event counters\n");
dev_err(dev, "could not get devfreq-event counters\n");
return dmc->num_counters;
}
dmc->counter = devm_kcalloc(dmc->dev, dmc->num_counters,
dmc->counter = devm_kcalloc(dev, dmc->num_counters,
sizeof(*dmc->counter), GFP_KERNEL);
if (!dmc->counter)
return -ENOMEM;
for (i = 0; i < dmc->num_counters; i++) {
dmc->counter[i] =
devfreq_event_get_edev_by_phandle(dmc->dev,
"devfreq-events", i);
devfreq_event_get_edev_by_phandle(dev, "devfreq-events", i);
if (IS_ERR_OR_NULL(dmc->counter[i]))
return -EPROBE_DEFER;
}
ret = exynos5_counters_enable_edev(dmc);
if (ret < 0) {
dev_err(dmc->dev, "could not enable event counter\n");
dev_err(dev, "could not enable event counter\n");
return ret;
}
ret = exynos5_counters_set_event(dmc);
if (ret < 0) {
exynos5_counters_disable_edev(dmc);
dev_err(dmc->dev, "could not set event counter\n");
dev_err(dev, "could not set event counter\n");
return ret;
}

View file

@ -1573,29 +1573,22 @@ static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi)
{
struct device *dev = ebi->dev;
struct device_node *child;
bool child_found = false;
u32 bank;
int ret;
for_each_available_child_of_node(dev->of_node, child) {
for_each_available_child_of_node_scoped(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &bank);
if (ret) {
dev_err(dev, "could not retrieve reg property: %d\n",
ret);
of_node_put(child);
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "could not retrieve reg property\n");
if (bank >= FMC2_MAX_BANKS) {
dev_err(dev, "invalid reg value: %d\n", bank);
of_node_put(child);
return -EINVAL;
}
if (ebi->bank_assigned & BIT(bank)) {
dev_err(dev, "bank already assigned: %d\n", bank);
of_node_put(child);
return -EINVAL;
}
@ -1603,19 +1596,15 @@ static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi)
ret = ebi->data->check_rif(ebi, bank + 1);
if (ret) {
dev_err(dev, "bank access failed: %d\n", bank);
of_node_put(child);
return ret;
}
}
if (bank < FMC2_MAX_EBI_CE) {
ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank);
if (ret) {
dev_err(dev, "setup chip select %d failed: %d\n",
bank, ret);
of_node_put(child);
return ret;
}
if (ret)
return dev_err_probe(dev, ret,
"setup chip select %d failed\n", bank);
}
ebi->bank_assigned |= BIT(bank);

View file

@ -450,7 +450,6 @@ static int load_one_timing(struct tegra_mc *mc,
static int load_timings(struct tegra_mc *mc, struct device_node *node)
{
struct device_node *child;
struct tegra_mc_timing *timing;
int child_count = of_get_child_count(node);
int i = 0, err;
@ -462,14 +461,12 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node)
mc->num_timings = child_count;
for_each_child_of_node(node, child) {
for_each_child_of_node_scoped(node, child) {
timing = &mc->timings[i++];
err = load_one_timing(mc, timing, child);
if (err) {
of_node_put(child);
if (err)
return err;
}
}
return 0;
@ -477,7 +474,6 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node)
static int tegra_mc_setup_timings(struct tegra_mc *mc)
{
struct device_node *node;
u32 ram_code, node_ram_code;
int err;
@ -485,14 +481,13 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc)
mc->num_timings = 0;
for_each_child_of_node(mc->dev->of_node, node) {
for_each_child_of_node_scoped(mc->dev->of_node, node) {
err = of_property_read_u32(node, "nvidia,ram-code",
&node_ram_code);
if (err || (node_ram_code != ram_code))
continue;
err = load_timings(mc, node);
of_node_put(node);
if (err)
return err;
break;

View file

@ -992,7 +992,6 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
struct device_node *node)
{
int child_count = of_get_child_count(node);
struct device_node *child;
struct emc_timing *timing;
unsigned int i = 0;
int err;
@ -1004,14 +1003,12 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
emc->num_timings = child_count;
for_each_child_of_node(node, child) {
for_each_child_of_node_scoped(node, child) {
timing = &emc->timings[i++];
err = load_one_timing_from_dt(emc, timing, child);
if (err) {
of_node_put(child);
if (err)
return err;
}
}
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,

View file

@ -35,11 +35,6 @@ struct tegra186_emc {
struct icc_provider provider;
};
static inline struct tegra186_emc *to_tegra186_emc(struct icc_provider *provider)
{
return container_of(provider, struct tegra186_emc, provider);
}
/*
* debugfs interface
*

View file

@ -410,7 +410,6 @@ static int cmp_timings(const void *_a, const void *_b)
static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
struct device_node *node)
{
struct device_node *child;
struct emc_timing *timing;
int child_count;
int err;
@ -428,15 +427,13 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
timing = emc->timings;
for_each_child_of_node(node, child) {
for_each_child_of_node_scoped(node, child) {
if (of_node_name_eq(child, "lpddr2"))
continue;
err = load_one_timing_from_dt(emc, timing++, child);
if (err) {
of_node_put(child);
if (err)
return err;
}
emc->num_timings++;
}

View file

@ -75,29 +75,29 @@ enum {
* The division portion of the average operation.
*/
#define __AVERAGE_PTFV(dev) \
({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] = \
next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \
({ next->ptfv_list[(dev)] = \
next->ptfv_list[(dev)] / \
next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
/*
* Convert val to fixed point and add it to the temporary average.
*/
#define __INCREMENT_PTFV(dev, val) \
({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] += \
({ next->ptfv_list[(dev)] += \
((val) * MOVAVG_PRECISION_FACTOR); })
/*
* Convert a moving average back to integral form and return the value.
*/
#define __MOVAVG_AC(timing, dev) \
((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \
((timing)->ptfv_list[(dev)] / \
MOVAVG_PRECISION_FACTOR)
/* Weighted update. */
#define __WEIGHTED_UPDATE_PTFV(dev, nval) \
do { \
int w = PTFV_MOVAVG_WEIGHT_INDEX; \
int dqs = PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX; \
int dqs = (dev); \
\
next->ptfv_list[dqs] = \
((nval * MOVAVG_PRECISION_FACTOR) + \
@ -105,315 +105,91 @@ enum {
next->ptfv_list[w])) / \
(next->ptfv_list[w] + 1); \
\
emc_dbg(emc, EMA_UPDATES, "%s: (s=%lu) EMA: %u\n", \
emc_dbg(emc, EMA_UPDATES, "%s: (s=%u) EMA: %u\n", \
__stringify(dev), nval, next->ptfv_list[dqs]); \
} while (0)
/* Access a particular average. */
#define __MOVAVG(timing, dev) \
((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX])
((timing)->ptfv_list[(dev)])
static u32 update_clock_tree_delay(struct tegra210_emc *emc, int type)
static bool tegra210_emc_compare_update_delay(struct tegra210_emc_timing *timing,
u32 measured, u32 idx)
{
bool periodic_training_update = type == PERIODIC_TRAINING_UPDATE;
struct tegra210_emc_timing *last = emc->last;
struct tegra210_emc_timing *next = emc->next;
u32 last_timing_rate_mhz = last->rate / 1000;
u32 next_timing_rate_mhz = next->rate / 1000;
bool dvfs_update = type == DVFS_UPDATE;
s32 tdel = 0, tmdel = 0, adel = 0;
bool dvfs_pt1 = type == DVFS_PT1;
unsigned long cval = 0;
u32 temp[2][2], value;
unsigned int i;
u32 *curr = &timing->current_dram_clktree[idx];
u32 rate_mhz = timing->rate / 1000;
u32 tmdel;
/*
* Dev0 MSB.
*/
if (dvfs_pt1 || periodic_training_update) {
value = tegra210_emc_mrr_read(emc, 2, 19);
tmdel = abs(*curr - measured);
for (i = 0; i < emc->num_channels; i++) {
temp[i][0] = (value & 0x00ff) << 8;
temp[i][1] = (value & 0xff00) << 0;
value >>= 16;
}
/*
* Dev0 LSB.
*/
value = tegra210_emc_mrr_read(emc, 2, 18);
for (i = 0; i < emc->num_channels; i++) {
temp[i][0] |= (value & 0x00ff) >> 0;
temp[i][1] |= (value & 0xff00) >> 8;
value >>= 16;
}
if (tmdel * 128 * rate_mhz / 1000000 > timing->tree_margin) {
*curr = measured;
return true;
}
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[0][0];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C0D0U0, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C0D0U0);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C0D0U0, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C0D0U0] -
__MOVAVG_AC(next, C0D0U0);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C0D0U0] =
__MOVAVG_AC(next, C0D0U0);
}
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[0][1];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C0D0U1, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C0D0U1);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C0D0U1, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C0D0U1] -
__MOVAVG_AC(next, C0D0U1);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
if (tmdel > adel)
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C0D0U1] =
__MOVAVG_AC(next, C0D0U1);
}
if (emc->num_channels > 1) {
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[1][0];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C1D0U0, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C1D0U0);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C1D0U0, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C1D0U0] -
__MOVAVG_AC(next, C1D0U0);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
if (tmdel > adel)
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C1D0U0] =
__MOVAVG_AC(next, C1D0U0);
}
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[1][1];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C1D0U1, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C1D0U1);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C1D0U1, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C1D0U1] -
__MOVAVG_AC(next, C1D0U1);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
if (tmdel > adel)
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C1D0U1] =
__MOVAVG_AC(next, C1D0U1);
}
}
if (emc->num_devices < 2)
goto done;
/*
* Dev1 MSB.
*/
if (dvfs_pt1 || periodic_training_update) {
value = tegra210_emc_mrr_read(emc, 1, 19);
for (i = 0; i < emc->num_channels; i++) {
temp[i][0] = (value & 0x00ff) << 8;
temp[i][1] = (value & 0xff00) << 0;
value >>= 16;
}
/*
* Dev1 LSB.
*/
value = tegra210_emc_mrr_read(emc, 1, 18);
for (i = 0; i < emc->num_channels; i++) {
temp[i][0] |= (value & 0x00ff) >> 0;
temp[i][1] |= (value & 0xff00) >> 8;
value >>= 16;
}
}
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[0][0];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C0D1U0, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C0D1U0);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C0D1U0, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C0D1U0] -
__MOVAVG_AC(next, C0D1U0);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
if (tmdel > adel)
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C0D1U0] =
__MOVAVG_AC(next, C0D1U0);
}
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[0][1];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C0D1U1, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C0D1U1);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C0D1U1, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C0D1U1] -
__MOVAVG_AC(next, C0D1U1);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
if (tmdel > adel)
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C0D1U1] =
__MOVAVG_AC(next, C0D1U1);
}
if (emc->num_channels > 1) {
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[1][0];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C1D1U0, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C1D1U0);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C1D1U0, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C1D1U0] -
__MOVAVG_AC(next, C1D1U0);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
if (tmdel > adel)
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C1D1U0] =
__MOVAVG_AC(next, C1D1U0);
}
if (dvfs_pt1 || periodic_training_update) {
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
cval *= 1000000;
cval /= last_timing_rate_mhz * 2 * temp[1][1];
}
if (dvfs_pt1)
__INCREMENT_PTFV(C1D1U1, cval);
else if (dvfs_update)
__AVERAGE_PTFV(C1D1U1);
else if (periodic_training_update)
__WEIGHTED_UPDATE_PTFV(C1D1U1, cval);
if (dvfs_update || periodic_training_update) {
tdel = next->current_dram_clktree[C1D1U1] -
__MOVAVG_AC(next, C1D1U1);
tmdel = (tdel < 0) ? -1 * tdel : tdel;
if (tmdel > adel)
adel = tmdel;
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
next->tree_margin)
next->current_dram_clktree[C1D1U1] =
__MOVAVG_AC(next, C1D1U1);
}
}
done:
return adel;
return false;
}
static u32 periodic_compensation_handler(struct tegra210_emc *emc, u32 type,
struct tegra210_emc_timing *last,
struct tegra210_emc_timing *next)
static void tegra210_emc_get_clktree_delay(struct tegra210_emc *emc,
u32 delay[DRAM_CLKTREE_NUM])
{
struct tegra210_emc_timing *curr = emc->last;
u32 rate_mhz = curr->rate / 1000;
u32 msb, lsb, dqsosc, delay_us;
unsigned int c, d, idx;
unsigned long clocks;
clocks = tegra210_emc_actual_osc_clocks(curr->run_clocks);
delay_us = 2 + (clocks / rate_mhz);
tegra210_emc_start_periodic_compensation(emc);
udelay(delay_us);
for (d = 0; d < emc->num_devices; d++) {
/* Read DQSOSC from MRR18/19 */
msb = tegra210_emc_mrr_read(emc, 2 - d, 19);
lsb = tegra210_emc_mrr_read(emc, 2 - d, 18);
for (c = 0; c < emc->num_channels; c++) {
/* C[c]D[d]U[0] */
idx = c * 4 + d * 2;
dqsosc = (msb & 0x00ff) << 8;
dqsosc |= (lsb & 0x00ff) >> 0;
/* Check for unpopulated channels */
if (dqsosc)
delay[idx] = (clocks * 1000000) /
(rate_mhz * 2 * dqsosc);
/* C[c]D[d]U[1] */
idx++;
dqsosc = (msb & 0xff00) << 0;
dqsosc |= (lsb & 0xff00) >> 8;
/* Check for unpopulated channels */
if (dqsosc)
delay[idx] = (clocks * 1000000) /
(rate_mhz * 2 * dqsosc);
msb >>= 16;
lsb >>= 16;
}
}
}
static bool periodic_compensation_handler(struct tegra210_emc *emc, u32 type,
struct tegra210_emc_timing *last,
struct tegra210_emc_timing *next)
{
#define __COPY_EMA(nt, lt, dev) \
({ __MOVAVG(nt, dev) = __MOVAVG(lt, dev) * \
(nt)->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
u32 i, adel = 0, samples = next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX];
u32 delay;
delay = tegra210_emc_actual_osc_clocks(last->run_clocks);
delay *= 1000;
delay = 2 + (delay / last->rate);
u32 i, samples = next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX];
u32 delay[DRAM_CLKTREE_NUM], idx;
bool over = false;
if (!next->periodic_training)
return 0;
@ -427,57 +203,46 @@ static u32 periodic_compensation_handler(struct tegra210_emc *emc, u32 type,
* calibration then we can reuse the previous
* frequencies EMA data.
*/
__COPY_EMA(next, last, C0D0U0);
__COPY_EMA(next, last, C0D0U1);
__COPY_EMA(next, last, C1D0U0);
__COPY_EMA(next, last, C1D0U1);
__COPY_EMA(next, last, C0D1U0);
__COPY_EMA(next, last, C0D1U1);
__COPY_EMA(next, last, C1D1U0);
__COPY_EMA(next, last, C1D1U1);
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++)
__COPY_EMA(next, last, idx);
} else {
/* Reset the EMA.*/
__MOVAVG(next, C0D0U0) = 0;
__MOVAVG(next, C0D0U1) = 0;
__MOVAVG(next, C1D0U0) = 0;
__MOVAVG(next, C1D0U1) = 0;
__MOVAVG(next, C0D1U0) = 0;
__MOVAVG(next, C0D1U1) = 0;
__MOVAVG(next, C1D1U0) = 0;
__MOVAVG(next, C1D1U1) = 0;
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++)
__MOVAVG(next, idx) = 0;
for (i = 0; i < samples; i++) {
tegra210_emc_start_periodic_compensation(emc);
udelay(delay);
/* Generate next sample of data. */
tegra210_emc_get_clktree_delay(emc, delay);
/*
* Generate next sample of data.
*/
adel = update_clock_tree_delay(emc, DVFS_PT1);
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++)
__INCREMENT_PTFV(idx, delay[idx]);
}
}
/*
* Seems like it should be part of the
* 'if (last_timing->periodic_training)' conditional
* since is already done for the else clause.
*/
adel = update_clock_tree_delay(emc, DVFS_UPDATE);
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++) {
/* Do the division part of the moving average */
__AVERAGE_PTFV(idx);
over |= tegra210_emc_compare_update_delay(next,
__MOVAVG_AC(next, idx), idx);
}
}
if (type == PERIODIC_TRAINING_SEQUENCE) {
tegra210_emc_start_periodic_compensation(emc);
udelay(delay);
tegra210_emc_get_clktree_delay(emc, delay);
adel = update_clock_tree_delay(emc, PERIODIC_TRAINING_UPDATE);
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++) {
__WEIGHTED_UPDATE_PTFV(idx, delay[idx]);
over |= tegra210_emc_compare_update_delay(next,
__MOVAVG_AC(next, idx), idx);
}
}
return adel;
return over;
}
static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc)
{
u32 emc_cfg, emc_cfg_o, emc_cfg_update, del, value;
u32 emc_cfg, emc_cfg_o, emc_cfg_update, value;
static const u32 list[] = {
EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0,
EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1,
@ -492,7 +257,6 @@ static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc)
};
struct tegra210_emc_timing *last = emc->last;
unsigned int items = ARRAY_SIZE(list), i;
unsigned long delay;
if (last->periodic_training) {
emc_dbg(emc, PER_TRAIN, "Periodic training starting\n");
@ -530,30 +294,18 @@ static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc)
/*
* 2. osc kick off - this assumes training and dvfs have set
* correct MR23.
*/
tegra210_emc_start_periodic_compensation(emc);
/*
*
* 3. Let dram capture its clock tree delays.
*/
delay = tegra210_emc_actual_osc_clocks(last->run_clocks);
delay *= 1000;
delay /= last->rate + 1;
udelay(delay);
/*
*
* 4. Check delta wrt previous values (save value if margin
* exceeds what is set in table).
*/
del = periodic_compensation_handler(emc,
PERIODIC_TRAINING_SEQUENCE,
last, last);
if (periodic_compensation_handler(emc, PERIODIC_TRAINING_SEQUENCE,
last, last)) {
/*
* 5. Apply compensation w.r.t. trained values (if clock tree
* has drifted more than the set margin).
*/
if (last->tree_margin < ((del * 128 * (last->rate / 1000)) / 1000000)) {
for (i = 0; i < items; i++) {
value = tegra210_emc_compensate(last, list[i]);
emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n",
@ -734,16 +486,7 @@ static void tegra210_emc_r21021_set_clock(struct tegra210_emc *emc, u32 clksrc)
EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK,
0);
tegra210_emc_start_periodic_compensation(emc);
delay = 1000 * tegra210_emc_actual_osc_clocks(last->run_clocks);
udelay((delay / last->rate) + 2);
value = periodic_compensation_handler(emc, DVFS_SEQUENCE, fake,
next);
value = (value * 128 * next->rate / 1000) / 1000000;
if (next->periodic_training && value > next->tree_margin)
if (periodic_compensation_handler(emc, DVFS_SEQUENCE, fake, next))
compensate_trimmer_applicable = true;
}

View file

@ -979,7 +979,6 @@ static int emc_check_mc_timings(struct tegra_emc *emc)
static int emc_load_timings_from_dt(struct tegra_emc *emc,
struct device_node *node)
{
struct device_node *child;
struct emc_timing *timing;
int child_count;
int err;
@ -998,12 +997,10 @@ static int emc_load_timings_from_dt(struct tegra_emc *emc,
emc->num_timings = child_count;
timing = emc->timings;
for_each_child_of_node(node, child) {
for_each_child_of_node_scoped(node, child) {
err = load_one_timing_from_dt(emc, timing++, child);
if (err) {
of_node_put(child);
if (err)
return err;
}
}
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,

View file

@ -17,7 +17,6 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/platform_data/ti-aemif.h>
#define TA_SHIFT 2
#define RHOLD_SHIFT 4
@ -330,42 +329,27 @@ static int aemif_probe(struct platform_device *pdev)
int ret = -ENODEV;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *child_np;
struct aemif_device *aemif;
struct aemif_platform_data *pdata;
struct of_dev_auxdata *dev_lookup;
aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL);
if (!aemif)
return -ENOMEM;
pdata = dev_get_platdata(&pdev->dev);
dev_lookup = pdata ? pdata->dev_lookup : NULL;
platform_set_drvdata(pdev, aemif);
aemif->clk = devm_clk_get(dev, NULL);
if (IS_ERR(aemif->clk)) {
dev_err(dev, "cannot get clock 'aemif'\n");
return PTR_ERR(aemif->clk);
}
ret = clk_prepare_enable(aemif->clk);
if (ret)
return ret;
aemif->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(aemif->clk))
return dev_err_probe(dev, PTR_ERR(aemif->clk),
"cannot get clock 'aemif'\n");
aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
if (np && of_device_is_compatible(np, "ti,da850-aemif"))
aemif->cs_offset = 2;
else if (pdata)
aemif->cs_offset = pdata->cs_offset;
aemif->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(aemif->base)) {
ret = PTR_ERR(aemif->base);
goto error;
}
if (IS_ERR(aemif->base))
return PTR_ERR(aemif->base);
if (np) {
/*
@ -374,17 +358,10 @@ static int aemif_probe(struct platform_device *pdev)
* functions iterate over these nodes and update the cs data
* array.
*/
for_each_available_child_of_node(np, child_np) {
for_each_available_child_of_node_scoped(np, child_np) {
ret = of_aemif_parse_abus_config(pdev, child_np);
if (ret < 0) {
of_node_put(child_np);
goto error;
}
}
} else if (pdata && pdata->num_abus_data > 0) {
for (i = 0; i < pdata->num_abus_data; i++, aemif->num_cs++) {
aemif->cs_data[i].cs = pdata->abus_data[i].cs;
aemif_get_hw_params(pdev, i);
if (ret < 0)
return ret;
}
}
@ -393,7 +370,7 @@ static int aemif_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(dev, "Error configuring chip select %d\n",
aemif->cs_data[i].cs);
goto error;
return ret;
}
}
@ -402,41 +379,18 @@ static int aemif_probe(struct platform_device *pdev)
* child will be probed after the AEMIF timing parameters are set.
*/
if (np) {
for_each_available_child_of_node(np, child_np) {
ret = of_platform_populate(child_np, NULL,
dev_lookup, dev);
if (ret < 0) {
of_node_put(child_np);
goto error;
}
}
} else if (pdata) {
for (i = 0; i < pdata->num_sub_devices; i++) {
pdata->sub_devices[i].dev.parent = dev;
ret = platform_device_register(&pdata->sub_devices[i]);
if (ret) {
dev_warn(dev, "Error register sub device %s\n",
pdata->sub_devices[i].name);
}
for_each_available_child_of_node_scoped(np, child_np) {
ret = of_platform_populate(child_np, NULL, NULL, dev);
if (ret < 0)
return ret;
}
}
return 0;
error:
clk_disable_unprepare(aemif->clk);
return ret;
}
static void aemif_remove(struct platform_device *pdev)
{
struct aemif_device *aemif = platform_get_drvdata(pdev);
clk_disable_unprepare(aemif->clk);
}
static struct platform_driver aemif_driver = {
.probe = aemif_probe,
.remove_new = aemif_remove,
.driver = {
.name = "ti-aemif",
.of_match_table = of_match_ptr(aemif_of_match),

View file

@ -1,45 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* TI DaVinci AEMIF platform glue.
*
* Copyright (C) 2017 BayLibre SAS
*
* Author:
* Bartosz Golaszewski <bgolaszewski@baylibre.com>
*/
#ifndef __TI_DAVINCI_AEMIF_DATA_H__
#define __TI_DAVINCI_AEMIF_DATA_H__
#include <linux/of_platform.h>
/**
* struct aemif_abus_data - Async bus configuration parameters.
*
* @cs - Chip-select number.
*/
struct aemif_abus_data {
u32 cs;
};
/**
* struct aemif_platform_data - Data to set up the TI aemif driver.
*
* @dev_lookup: of_dev_auxdata passed to of_platform_populate() for aemif
* subdevices.
* @cs_offset: Lowest allowed chip-select number.
* @abus_data: Array of async bus configuration entries.
* @num_abus_data: Number of abus entries.
* @sub_devices: Array of platform subdevices.
* @num_sub_devices: Number of subdevices.
*/
struct aemif_platform_data {
struct of_dev_auxdata *dev_lookup;
u32 cs_offset;
struct aemif_abus_data *abus_data;
size_t num_abus_data;
struct platform_device *sub_devices;
size_t num_sub_devices;
};
#endif /* __TI_DAVINCI_AEMIF_DATA_H__ */