162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Memory controller driver for ARM PrimeCell PL172 462306a36Sopenharmony_ci * PrimeCell MultiPort Memory Controller (PL172) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on: 962306a36Sopenharmony_ci * TI AEMIF driver, Copyright (C) 2010 - 2013 Texas Instruments Inc. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/amba/bus.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/time.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MPMC_STATIC_CFG(n) (0x200 + 0x20 * (n)) 2562306a36Sopenharmony_ci#define MPMC_STATIC_CFG_MW_8BIT 0x0 2662306a36Sopenharmony_ci#define MPMC_STATIC_CFG_MW_16BIT 0x1 2762306a36Sopenharmony_ci#define MPMC_STATIC_CFG_MW_32BIT 0x2 2862306a36Sopenharmony_ci#define MPMC_STATIC_CFG_PM BIT(3) 2962306a36Sopenharmony_ci#define MPMC_STATIC_CFG_PC BIT(6) 3062306a36Sopenharmony_ci#define MPMC_STATIC_CFG_PB BIT(7) 3162306a36Sopenharmony_ci#define MPMC_STATIC_CFG_EW BIT(8) 3262306a36Sopenharmony_ci#define MPMC_STATIC_CFG_B BIT(19) 3362306a36Sopenharmony_ci#define MPMC_STATIC_CFG_P BIT(20) 3462306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_WEN(n) (0x204 + 0x20 * (n)) 3562306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_WEN_MAX 0x0f 3662306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_OEN(n) (0x208 + 0x20 * (n)) 3762306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_OEN_MAX 0x0f 3862306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_RD(n) (0x20c + 0x20 * (n)) 3962306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_RD_MAX 0x1f 4062306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_PAGE(n) (0x210 + 0x20 * (n)) 4162306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_PAGE_MAX 0x1f 4262306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_WR(n) (0x214 + 0x20 * (n)) 4362306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_WR_MAX 0x1f 4462306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_TURN(n) (0x218 + 0x20 * (n)) 4562306a36Sopenharmony_ci#define MPMC_STATIC_WAIT_TURN_MAX 0x0f 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Maximum number of static chip selects */ 4862306a36Sopenharmony_ci#define PL172_MAX_CS 4 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct pl172_data { 5162306a36Sopenharmony_ci void __iomem *base; 5262306a36Sopenharmony_ci unsigned long rate; 5362306a36Sopenharmony_ci struct clk *clk; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int pl172_timing_prop(struct amba_device *adev, 5762306a36Sopenharmony_ci const struct device_node *np, const char *name, 5862306a36Sopenharmony_ci u32 reg_offset, u32 max, int start) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct pl172_data *pl172 = amba_get_drvdata(adev); 6162306a36Sopenharmony_ci int cycles; 6262306a36Sopenharmony_ci u32 val; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!of_property_read_u32(np, name, &val)) { 6562306a36Sopenharmony_ci cycles = DIV_ROUND_UP(val * pl172->rate, NSEC_PER_MSEC) - start; 6662306a36Sopenharmony_ci if (cycles < 0) { 6762306a36Sopenharmony_ci cycles = 0; 6862306a36Sopenharmony_ci } else if (cycles > max) { 6962306a36Sopenharmony_ci dev_err(&adev->dev, "%s timing too tight\n", name); 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci writel(cycles, pl172->base + reg_offset); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci dev_dbg(&adev->dev, "%s: %u cycle(s)\n", name, start + 7762306a36Sopenharmony_ci readl(pl172->base + reg_offset)); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int pl172_setup_static(struct amba_device *adev, 8362306a36Sopenharmony_ci struct device_node *np, u32 cs) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct pl172_data *pl172 = amba_get_drvdata(adev); 8662306a36Sopenharmony_ci u32 cfg; 8762306a36Sopenharmony_ci int ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* MPMC static memory configuration */ 9062306a36Sopenharmony_ci if (!of_property_read_u32(np, "mpmc,memory-width", &cfg)) { 9162306a36Sopenharmony_ci if (cfg == 8) { 9262306a36Sopenharmony_ci cfg = MPMC_STATIC_CFG_MW_8BIT; 9362306a36Sopenharmony_ci } else if (cfg == 16) { 9462306a36Sopenharmony_ci cfg = MPMC_STATIC_CFG_MW_16BIT; 9562306a36Sopenharmony_ci } else if (cfg == 32) { 9662306a36Sopenharmony_ci cfg = MPMC_STATIC_CFG_MW_32BIT; 9762306a36Sopenharmony_ci } else { 9862306a36Sopenharmony_ci dev_err(&adev->dev, "invalid memory width cs%u\n", cs); 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci } else { 10262306a36Sopenharmony_ci dev_err(&adev->dev, "memory-width property required\n"); 10362306a36Sopenharmony_ci return -EINVAL; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (of_property_read_bool(np, "mpmc,async-page-mode")) 10762306a36Sopenharmony_ci cfg |= MPMC_STATIC_CFG_PM; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (of_property_read_bool(np, "mpmc,cs-active-high")) 11062306a36Sopenharmony_ci cfg |= MPMC_STATIC_CFG_PC; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (of_property_read_bool(np, "mpmc,byte-lane-low")) 11362306a36Sopenharmony_ci cfg |= MPMC_STATIC_CFG_PB; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (of_property_read_bool(np, "mpmc,extended-wait")) 11662306a36Sopenharmony_ci cfg |= MPMC_STATIC_CFG_EW; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (amba_part(adev) == 0x172 && 11962306a36Sopenharmony_ci of_property_read_bool(np, "mpmc,buffer-enable")) 12062306a36Sopenharmony_ci cfg |= MPMC_STATIC_CFG_B; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (of_property_read_bool(np, "mpmc,write-protect")) 12362306a36Sopenharmony_ci cfg |= MPMC_STATIC_CFG_P; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci writel(cfg, pl172->base + MPMC_STATIC_CFG(cs)); 12662306a36Sopenharmony_ci dev_dbg(&adev->dev, "mpmc static config cs%u: 0x%08x\n", cs, cfg); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* MPMC static memory timing */ 12962306a36Sopenharmony_ci ret = pl172_timing_prop(adev, np, "mpmc,write-enable-delay", 13062306a36Sopenharmony_ci MPMC_STATIC_WAIT_WEN(cs), 13162306a36Sopenharmony_ci MPMC_STATIC_WAIT_WEN_MAX, 1); 13262306a36Sopenharmony_ci if (ret) 13362306a36Sopenharmony_ci goto fail; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ret = pl172_timing_prop(adev, np, "mpmc,output-enable-delay", 13662306a36Sopenharmony_ci MPMC_STATIC_WAIT_OEN(cs), 13762306a36Sopenharmony_ci MPMC_STATIC_WAIT_OEN_MAX, 0); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci goto fail; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = pl172_timing_prop(adev, np, "mpmc,read-access-delay", 14262306a36Sopenharmony_ci MPMC_STATIC_WAIT_RD(cs), 14362306a36Sopenharmony_ci MPMC_STATIC_WAIT_RD_MAX, 1); 14462306a36Sopenharmony_ci if (ret) 14562306a36Sopenharmony_ci goto fail; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = pl172_timing_prop(adev, np, "mpmc,page-mode-read-delay", 14862306a36Sopenharmony_ci MPMC_STATIC_WAIT_PAGE(cs), 14962306a36Sopenharmony_ci MPMC_STATIC_WAIT_PAGE_MAX, 1); 15062306a36Sopenharmony_ci if (ret) 15162306a36Sopenharmony_ci goto fail; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ret = pl172_timing_prop(adev, np, "mpmc,write-access-delay", 15462306a36Sopenharmony_ci MPMC_STATIC_WAIT_WR(cs), 15562306a36Sopenharmony_ci MPMC_STATIC_WAIT_WR_MAX, 2); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci goto fail; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = pl172_timing_prop(adev, np, "mpmc,turn-round-delay", 16062306a36Sopenharmony_ci MPMC_STATIC_WAIT_TURN(cs), 16162306a36Sopenharmony_ci MPMC_STATIC_WAIT_TURN_MAX, 1); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci goto fail; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_cifail: 16762306a36Sopenharmony_ci dev_err(&adev->dev, "failed to configure cs%u\n", cs); 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int pl172_parse_cs_config(struct amba_device *adev, 17262306a36Sopenharmony_ci struct device_node *np) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci u32 cs; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!of_property_read_u32(np, "mpmc,cs", &cs)) { 17762306a36Sopenharmony_ci if (cs >= PL172_MAX_CS) { 17862306a36Sopenharmony_ci dev_err(&adev->dev, "cs%u invalid\n", cs); 17962306a36Sopenharmony_ci return -EINVAL; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return pl172_setup_static(adev, np, cs); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci dev_err(&adev->dev, "cs property required\n"); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const char * const pl172_revisions[] = {"r1", "r2", "r2p3", "r2p4"}; 19162306a36Sopenharmony_cistatic const char * const pl175_revisions[] = {"r1"}; 19262306a36Sopenharmony_cistatic const char * const pl176_revisions[] = {"r0"}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int pl172_probe(struct amba_device *adev, const struct amba_id *id) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct device_node *child_np, *np = adev->dev.of_node; 19762306a36Sopenharmony_ci struct device *dev = &adev->dev; 19862306a36Sopenharmony_ci static const char *rev = "?"; 19962306a36Sopenharmony_ci struct pl172_data *pl172; 20062306a36Sopenharmony_ci int ret; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (amba_part(adev) == 0x172) { 20362306a36Sopenharmony_ci if (amba_rev(adev) < ARRAY_SIZE(pl172_revisions)) 20462306a36Sopenharmony_ci rev = pl172_revisions[amba_rev(adev)]; 20562306a36Sopenharmony_ci } else if (amba_part(adev) == 0x175) { 20662306a36Sopenharmony_ci if (amba_rev(adev) < ARRAY_SIZE(pl175_revisions)) 20762306a36Sopenharmony_ci rev = pl175_revisions[amba_rev(adev)]; 20862306a36Sopenharmony_ci } else if (amba_part(adev) == 0x176) { 20962306a36Sopenharmony_ci if (amba_rev(adev) < ARRAY_SIZE(pl176_revisions)) 21062306a36Sopenharmony_ci rev = pl176_revisions[amba_rev(adev)]; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci dev_info(dev, "ARM PL%x revision %s\n", amba_part(adev), rev); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci pl172 = devm_kzalloc(dev, sizeof(*pl172), GFP_KERNEL); 21662306a36Sopenharmony_ci if (!pl172) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci pl172->clk = devm_clk_get(dev, "mpmcclk"); 22062306a36Sopenharmony_ci if (IS_ERR(pl172->clk)) { 22162306a36Sopenharmony_ci dev_err(dev, "no mpmcclk provided clock\n"); 22262306a36Sopenharmony_ci return PTR_ERR(pl172->clk); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ret = clk_prepare_enable(pl172->clk); 22662306a36Sopenharmony_ci if (ret) { 22762306a36Sopenharmony_ci dev_err(dev, "unable to mpmcclk enable clock\n"); 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci pl172->rate = clk_get_rate(pl172->clk) / MSEC_PER_SEC; 23262306a36Sopenharmony_ci if (!pl172->rate) { 23362306a36Sopenharmony_ci dev_err(dev, "unable to get mpmcclk clock rate\n"); 23462306a36Sopenharmony_ci ret = -EINVAL; 23562306a36Sopenharmony_ci goto err_clk_enable; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = amba_request_regions(adev, NULL); 23962306a36Sopenharmony_ci if (ret) { 24062306a36Sopenharmony_ci dev_err(dev, "unable to request AMBA regions\n"); 24162306a36Sopenharmony_ci goto err_clk_enable; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci pl172->base = devm_ioremap(dev, adev->res.start, 24562306a36Sopenharmony_ci resource_size(&adev->res)); 24662306a36Sopenharmony_ci if (!pl172->base) { 24762306a36Sopenharmony_ci dev_err(dev, "ioremap failed\n"); 24862306a36Sopenharmony_ci ret = -ENOMEM; 24962306a36Sopenharmony_ci goto err_no_ioremap; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci amba_set_drvdata(adev, pl172); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * Loop through each child node, which represent a chip select, and 25662306a36Sopenharmony_ci * configure parameters and timing. If successful; populate devices 25762306a36Sopenharmony_ci * under that node. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci for_each_available_child_of_node(np, child_np) { 26062306a36Sopenharmony_ci ret = pl172_parse_cs_config(adev, child_np); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci continue; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci of_platform_populate(child_np, NULL, NULL, dev); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cierr_no_ioremap: 27062306a36Sopenharmony_ci amba_release_regions(adev); 27162306a36Sopenharmony_cierr_clk_enable: 27262306a36Sopenharmony_ci clk_disable_unprepare(pl172->clk); 27362306a36Sopenharmony_ci return ret; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void pl172_remove(struct amba_device *adev) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct pl172_data *pl172 = amba_get_drvdata(adev); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci clk_disable_unprepare(pl172->clk); 28162306a36Sopenharmony_ci amba_release_regions(adev); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic const struct amba_id pl172_ids[] = { 28562306a36Sopenharmony_ci /* PrimeCell MPMC PL172, EMC found on NXP LPC18xx and LPC43xx */ 28662306a36Sopenharmony_ci { 28762306a36Sopenharmony_ci .id = 0x07041172, 28862306a36Sopenharmony_ci .mask = 0x3f0fffff, 28962306a36Sopenharmony_ci }, 29062306a36Sopenharmony_ci /* PrimeCell MPMC PL175, EMC found on NXP LPC32xx */ 29162306a36Sopenharmony_ci { 29262306a36Sopenharmony_ci .id = 0x07041175, 29362306a36Sopenharmony_ci .mask = 0x3f0fffff, 29462306a36Sopenharmony_ci }, 29562306a36Sopenharmony_ci /* PrimeCell MPMC PL176 */ 29662306a36Sopenharmony_ci { 29762306a36Sopenharmony_ci .id = 0x89041176, 29862306a36Sopenharmony_ci .mask = 0xff0fffff, 29962306a36Sopenharmony_ci }, 30062306a36Sopenharmony_ci { 0, 0 }, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl172_ids); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic struct amba_driver pl172_driver = { 30562306a36Sopenharmony_ci .drv = { 30662306a36Sopenharmony_ci .name = "memory-pl172", 30762306a36Sopenharmony_ci }, 30862306a36Sopenharmony_ci .probe = pl172_probe, 30962306a36Sopenharmony_ci .remove = pl172_remove, 31062306a36Sopenharmony_ci .id_table = pl172_ids, 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_cimodule_amba_driver(pl172_driver); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ciMODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); 31562306a36Sopenharmony_ciMODULE_DESCRIPTION("PL172 Memory Controller Driver"); 31662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 317