162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Qualcomm External Bus Interface 2 (EBI2) driver 462306a36Sopenharmony_ci * an older version of the Qualcomm Parallel Interface Controller (QPIC) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2016 Linaro Ltd. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * See the device tree bindings for this block for more details on the 1162306a36Sopenharmony_ci * hardware. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/clk.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_platform.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/bitops.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * CS0, CS1, CS4 and CS5 are two bits wide, CS2 and CS3 are one bit. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci#define EBI2_CS0_ENABLE_MASK BIT(0)|BIT(1) 2962306a36Sopenharmony_ci#define EBI2_CS1_ENABLE_MASK BIT(2)|BIT(3) 3062306a36Sopenharmony_ci#define EBI2_CS2_ENABLE_MASK BIT(4) 3162306a36Sopenharmony_ci#define EBI2_CS3_ENABLE_MASK BIT(5) 3262306a36Sopenharmony_ci#define EBI2_CS4_ENABLE_MASK BIT(6)|BIT(7) 3362306a36Sopenharmony_ci#define EBI2_CS5_ENABLE_MASK BIT(8)|BIT(9) 3462306a36Sopenharmony_ci#define EBI2_CSN_MASK GENMASK(9, 0) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define EBI2_XMEM_CFG 0x0000 /* Power management etc */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * SLOW CSn CFG 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Bits 31-28: RECOVERY recovery cycles (0 = 1, 1 = 2 etc) this is the time the 4262306a36Sopenharmony_ci * memory continues to drive the data bus after OE is de-asserted. 4362306a36Sopenharmony_ci * Inserted when reading one CS and switching to another CS or read 4462306a36Sopenharmony_ci * followed by write on the same CS. Valid values 0 thru 15. 4562306a36Sopenharmony_ci * Bits 27-24: WR_HOLD write hold cycles, these are extra cycles inserted after 4662306a36Sopenharmony_ci * every write minimum 1. The data out is driven from the time WE is 4762306a36Sopenharmony_ci * asserted until CS is asserted. With a hold of 1, the CS stays 4862306a36Sopenharmony_ci * active for 1 extra cycle etc. Valid values 0 thru 15. 4962306a36Sopenharmony_ci * Bits 23-16: WR_DELTA initial latency for write cycles inserted for the first 5062306a36Sopenharmony_ci * write to a page or burst memory 5162306a36Sopenharmony_ci * Bits 15-8: RD_DELTA initial latency for read cycles inserted for the first 5262306a36Sopenharmony_ci * read to a page or burst memory 5362306a36Sopenharmony_ci * Bits 7-4: WR_WAIT number of wait cycles for every write access, 0=1 cycle 5462306a36Sopenharmony_ci * so 1 thru 16 cycles. 5562306a36Sopenharmony_ci * Bits 3-0: RD_WAIT number of wait cycles for every read access, 0=1 cycle 5662306a36Sopenharmony_ci * so 1 thru 16 cycles. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci#define EBI2_XMEM_CS0_SLOW_CFG 0x0008 5962306a36Sopenharmony_ci#define EBI2_XMEM_CS1_SLOW_CFG 0x000C 6062306a36Sopenharmony_ci#define EBI2_XMEM_CS2_SLOW_CFG 0x0010 6162306a36Sopenharmony_ci#define EBI2_XMEM_CS3_SLOW_CFG 0x0014 6262306a36Sopenharmony_ci#define EBI2_XMEM_CS4_SLOW_CFG 0x0018 6362306a36Sopenharmony_ci#define EBI2_XMEM_CS5_SLOW_CFG 0x001C 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define EBI2_XMEM_RECOVERY_SHIFT 28 6662306a36Sopenharmony_ci#define EBI2_XMEM_WR_HOLD_SHIFT 24 6762306a36Sopenharmony_ci#define EBI2_XMEM_WR_DELTA_SHIFT 16 6862306a36Sopenharmony_ci#define EBI2_XMEM_RD_DELTA_SHIFT 8 6962306a36Sopenharmony_ci#define EBI2_XMEM_WR_WAIT_SHIFT 4 7062306a36Sopenharmony_ci#define EBI2_XMEM_RD_WAIT_SHIFT 0 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * FAST CSn CFG 7462306a36Sopenharmony_ci * Bits 31-28: ? 7562306a36Sopenharmony_ci * Bits 27-24: RD_HOLD: the length in cycles of the first segment of a read 7662306a36Sopenharmony_ci * transfer. For a single read trandfer this will be the time 7762306a36Sopenharmony_ci * from CS assertion to OE assertion. 7862306a36Sopenharmony_ci * Bits 18-24: ? 7962306a36Sopenharmony_ci * Bits 17-16: ADV_OE_RECOVERY, the number of cycles elapsed before an OE 8062306a36Sopenharmony_ci * assertion, with respect to the cycle where ADV is asserted. 8162306a36Sopenharmony_ci * 2 means 2 cycles between ADV and OE. Values 0, 1, 2 or 3. 8262306a36Sopenharmony_ci * Bits 5: ADDR_HOLD_ENA, The address is held for an extra cycle to meet 8362306a36Sopenharmony_ci * hold time requirements with ADV assertion. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * The manual mentions "write precharge cycles" and "precharge cycles". 8662306a36Sopenharmony_ci * We have not been able to figure out which bit fields these correspond to 8762306a36Sopenharmony_ci * in the hardware, or what valid values exist. The current hypothesis is that 8862306a36Sopenharmony_ci * this is something just used on the FAST chip selects. There is also a "byte 8962306a36Sopenharmony_ci * device enable" flag somewhere for 8bit memories. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci#define EBI2_XMEM_CS0_FAST_CFG 0x0028 9262306a36Sopenharmony_ci#define EBI2_XMEM_CS1_FAST_CFG 0x002C 9362306a36Sopenharmony_ci#define EBI2_XMEM_CS2_FAST_CFG 0x0030 9462306a36Sopenharmony_ci#define EBI2_XMEM_CS3_FAST_CFG 0x0034 9562306a36Sopenharmony_ci#define EBI2_XMEM_CS4_FAST_CFG 0x0038 9662306a36Sopenharmony_ci#define EBI2_XMEM_CS5_FAST_CFG 0x003C 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define EBI2_XMEM_RD_HOLD_SHIFT 24 9962306a36Sopenharmony_ci#define EBI2_XMEM_ADV_OE_RECOVERY_SHIFT 16 10062306a36Sopenharmony_ci#define EBI2_XMEM_ADDR_HOLD_ENA_SHIFT 5 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/** 10362306a36Sopenharmony_ci * struct cs_data - struct with info on a chipselect setting 10462306a36Sopenharmony_ci * @enable_mask: mask to enable the chipselect in the EBI2 config 10562306a36Sopenharmony_ci * @slow_cfg: offset to XMEMC slow CS config 10662306a36Sopenharmony_ci * @fast_cfg: offset to XMEMC fast CS config 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistruct cs_data { 10962306a36Sopenharmony_ci u32 enable_mask; 11062306a36Sopenharmony_ci u16 slow_cfg; 11162306a36Sopenharmony_ci u16 fast_cfg; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const struct cs_data cs_info[] = { 11562306a36Sopenharmony_ci { 11662306a36Sopenharmony_ci /* CS0 */ 11762306a36Sopenharmony_ci .enable_mask = EBI2_CS0_ENABLE_MASK, 11862306a36Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS0_SLOW_CFG, 11962306a36Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS0_FAST_CFG, 12062306a36Sopenharmony_ci }, 12162306a36Sopenharmony_ci { 12262306a36Sopenharmony_ci /* CS1 */ 12362306a36Sopenharmony_ci .enable_mask = EBI2_CS1_ENABLE_MASK, 12462306a36Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS1_SLOW_CFG, 12562306a36Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS1_FAST_CFG, 12662306a36Sopenharmony_ci }, 12762306a36Sopenharmony_ci { 12862306a36Sopenharmony_ci /* CS2 */ 12962306a36Sopenharmony_ci .enable_mask = EBI2_CS2_ENABLE_MASK, 13062306a36Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS2_SLOW_CFG, 13162306a36Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS2_FAST_CFG, 13262306a36Sopenharmony_ci }, 13362306a36Sopenharmony_ci { 13462306a36Sopenharmony_ci /* CS3 */ 13562306a36Sopenharmony_ci .enable_mask = EBI2_CS3_ENABLE_MASK, 13662306a36Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS3_SLOW_CFG, 13762306a36Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS3_FAST_CFG, 13862306a36Sopenharmony_ci }, 13962306a36Sopenharmony_ci { 14062306a36Sopenharmony_ci /* CS4 */ 14162306a36Sopenharmony_ci .enable_mask = EBI2_CS4_ENABLE_MASK, 14262306a36Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS4_SLOW_CFG, 14362306a36Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS4_FAST_CFG, 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci { 14662306a36Sopenharmony_ci /* CS5 */ 14762306a36Sopenharmony_ci .enable_mask = EBI2_CS5_ENABLE_MASK, 14862306a36Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS5_SLOW_CFG, 14962306a36Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS5_FAST_CFG, 15062306a36Sopenharmony_ci }, 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * struct ebi2_xmem_prop - describes an XMEM config property 15562306a36Sopenharmony_ci * @prop: the device tree binding name 15662306a36Sopenharmony_ci * @max: maximum value for the property 15762306a36Sopenharmony_ci * @slowreg: true if this property is in the SLOW CS config register 15862306a36Sopenharmony_ci * else it is assumed to be in the FAST config register 15962306a36Sopenharmony_ci * @shift: the bit field start in the SLOW or FAST register for this 16062306a36Sopenharmony_ci * property 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistruct ebi2_xmem_prop { 16362306a36Sopenharmony_ci const char *prop; 16462306a36Sopenharmony_ci u32 max; 16562306a36Sopenharmony_ci bool slowreg; 16662306a36Sopenharmony_ci u16 shift; 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct ebi2_xmem_prop xmem_props[] = { 17062306a36Sopenharmony_ci { 17162306a36Sopenharmony_ci .prop = "qcom,xmem-recovery-cycles", 17262306a36Sopenharmony_ci .max = 15, 17362306a36Sopenharmony_ci .slowreg = true, 17462306a36Sopenharmony_ci .shift = EBI2_XMEM_RECOVERY_SHIFT, 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci { 17762306a36Sopenharmony_ci .prop = "qcom,xmem-write-hold-cycles", 17862306a36Sopenharmony_ci .max = 15, 17962306a36Sopenharmony_ci .slowreg = true, 18062306a36Sopenharmony_ci .shift = EBI2_XMEM_WR_HOLD_SHIFT, 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci { 18362306a36Sopenharmony_ci .prop = "qcom,xmem-write-delta-cycles", 18462306a36Sopenharmony_ci .max = 255, 18562306a36Sopenharmony_ci .slowreg = true, 18662306a36Sopenharmony_ci .shift = EBI2_XMEM_WR_DELTA_SHIFT, 18762306a36Sopenharmony_ci }, 18862306a36Sopenharmony_ci { 18962306a36Sopenharmony_ci .prop = "qcom,xmem-read-delta-cycles", 19062306a36Sopenharmony_ci .max = 255, 19162306a36Sopenharmony_ci .slowreg = true, 19262306a36Sopenharmony_ci .shift = EBI2_XMEM_RD_DELTA_SHIFT, 19362306a36Sopenharmony_ci }, 19462306a36Sopenharmony_ci { 19562306a36Sopenharmony_ci .prop = "qcom,xmem-write-wait-cycles", 19662306a36Sopenharmony_ci .max = 15, 19762306a36Sopenharmony_ci .slowreg = true, 19862306a36Sopenharmony_ci .shift = EBI2_XMEM_WR_WAIT_SHIFT, 19962306a36Sopenharmony_ci }, 20062306a36Sopenharmony_ci { 20162306a36Sopenharmony_ci .prop = "qcom,xmem-read-wait-cycles", 20262306a36Sopenharmony_ci .max = 15, 20362306a36Sopenharmony_ci .slowreg = true, 20462306a36Sopenharmony_ci .shift = EBI2_XMEM_RD_WAIT_SHIFT, 20562306a36Sopenharmony_ci }, 20662306a36Sopenharmony_ci { 20762306a36Sopenharmony_ci .prop = "qcom,xmem-address-hold-enable", 20862306a36Sopenharmony_ci .max = 1, /* boolean prop */ 20962306a36Sopenharmony_ci .slowreg = false, 21062306a36Sopenharmony_ci .shift = EBI2_XMEM_ADDR_HOLD_ENA_SHIFT, 21162306a36Sopenharmony_ci }, 21262306a36Sopenharmony_ci { 21362306a36Sopenharmony_ci .prop = "qcom,xmem-adv-to-oe-recovery-cycles", 21462306a36Sopenharmony_ci .max = 3, 21562306a36Sopenharmony_ci .slowreg = false, 21662306a36Sopenharmony_ci .shift = EBI2_XMEM_ADV_OE_RECOVERY_SHIFT, 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci { 21962306a36Sopenharmony_ci .prop = "qcom,xmem-read-hold-cycles", 22062306a36Sopenharmony_ci .max = 15, 22162306a36Sopenharmony_ci .slowreg = false, 22262306a36Sopenharmony_ci .shift = EBI2_XMEM_RD_HOLD_SHIFT, 22362306a36Sopenharmony_ci }, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void qcom_ebi2_setup_chipselect(struct device_node *np, 22762306a36Sopenharmony_ci struct device *dev, 22862306a36Sopenharmony_ci void __iomem *ebi2_base, 22962306a36Sopenharmony_ci void __iomem *ebi2_xmem, 23062306a36Sopenharmony_ci u32 csindex) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci const struct cs_data *csd; 23362306a36Sopenharmony_ci u32 slowcfg, fastcfg; 23462306a36Sopenharmony_ci u32 val; 23562306a36Sopenharmony_ci int ret; 23662306a36Sopenharmony_ci int i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci csd = &cs_info[csindex]; 23962306a36Sopenharmony_ci val = readl(ebi2_base); 24062306a36Sopenharmony_ci val |= csd->enable_mask; 24162306a36Sopenharmony_ci writel(val, ebi2_base); 24262306a36Sopenharmony_ci dev_dbg(dev, "enabled CS%u\n", csindex); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Next set up the XMEMC */ 24562306a36Sopenharmony_ci slowcfg = 0; 24662306a36Sopenharmony_ci fastcfg = 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xmem_props); i++) { 24962306a36Sopenharmony_ci const struct ebi2_xmem_prop *xp = &xmem_props[i]; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* All are regular u32 values */ 25262306a36Sopenharmony_ci ret = of_property_read_u32(np, xp->prop, &val); 25362306a36Sopenharmony_ci if (ret) { 25462306a36Sopenharmony_ci dev_dbg(dev, "could not read %s for CS%d\n", 25562306a36Sopenharmony_ci xp->prop, csindex); 25662306a36Sopenharmony_ci continue; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* First check boolean props */ 26062306a36Sopenharmony_ci if (xp->max == 1 && val) { 26162306a36Sopenharmony_ci if (xp->slowreg) 26262306a36Sopenharmony_ci slowcfg |= BIT(xp->shift); 26362306a36Sopenharmony_ci else 26462306a36Sopenharmony_ci fastcfg |= BIT(xp->shift); 26562306a36Sopenharmony_ci dev_dbg(dev, "set %s flag\n", xp->prop); 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* We're dealing with an u32 */ 27062306a36Sopenharmony_ci if (val > xp->max) { 27162306a36Sopenharmony_ci dev_err(dev, 27262306a36Sopenharmony_ci "too high value for %s: %u, capped at %u\n", 27362306a36Sopenharmony_ci xp->prop, val, xp->max); 27462306a36Sopenharmony_ci val = xp->max; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci if (xp->slowreg) 27762306a36Sopenharmony_ci slowcfg |= (val << xp->shift); 27862306a36Sopenharmony_ci else 27962306a36Sopenharmony_ci fastcfg |= (val << xp->shift); 28062306a36Sopenharmony_ci dev_dbg(dev, "set %s to %u\n", xp->prop, val); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci dev_info(dev, "CS%u: SLOW CFG 0x%08x, FAST CFG 0x%08x\n", 28462306a36Sopenharmony_ci csindex, slowcfg, fastcfg); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (slowcfg) 28762306a36Sopenharmony_ci writel(slowcfg, ebi2_xmem + csd->slow_cfg); 28862306a36Sopenharmony_ci if (fastcfg) 28962306a36Sopenharmony_ci writel(fastcfg, ebi2_xmem + csd->fast_cfg); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int qcom_ebi2_probe(struct platform_device *pdev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 29562306a36Sopenharmony_ci struct device_node *child; 29662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 29762306a36Sopenharmony_ci struct resource *res; 29862306a36Sopenharmony_ci void __iomem *ebi2_base; 29962306a36Sopenharmony_ci void __iomem *ebi2_xmem; 30062306a36Sopenharmony_ci struct clk *ebi2xclk; 30162306a36Sopenharmony_ci struct clk *ebi2clk; 30262306a36Sopenharmony_ci bool have_children = false; 30362306a36Sopenharmony_ci u32 val; 30462306a36Sopenharmony_ci int ret; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ebi2xclk = devm_clk_get(dev, "ebi2x"); 30762306a36Sopenharmony_ci if (IS_ERR(ebi2xclk)) 30862306a36Sopenharmony_ci return PTR_ERR(ebi2xclk); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = clk_prepare_enable(ebi2xclk); 31162306a36Sopenharmony_ci if (ret) { 31262306a36Sopenharmony_ci dev_err(dev, "could not enable EBI2X clk (%d)\n", ret); 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ebi2clk = devm_clk_get(dev, "ebi2"); 31762306a36Sopenharmony_ci if (IS_ERR(ebi2clk)) { 31862306a36Sopenharmony_ci ret = PTR_ERR(ebi2clk); 31962306a36Sopenharmony_ci goto err_disable_2x_clk; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ret = clk_prepare_enable(ebi2clk); 32362306a36Sopenharmony_ci if (ret) { 32462306a36Sopenharmony_ci dev_err(dev, "could not enable EBI2 clk\n"); 32562306a36Sopenharmony_ci goto err_disable_2x_clk; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 32962306a36Sopenharmony_ci ebi2_base = devm_ioremap_resource(dev, res); 33062306a36Sopenharmony_ci if (IS_ERR(ebi2_base)) { 33162306a36Sopenharmony_ci ret = PTR_ERR(ebi2_base); 33262306a36Sopenharmony_ci goto err_disable_clk; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 33662306a36Sopenharmony_ci ebi2_xmem = devm_ioremap_resource(dev, res); 33762306a36Sopenharmony_ci if (IS_ERR(ebi2_xmem)) { 33862306a36Sopenharmony_ci ret = PTR_ERR(ebi2_xmem); 33962306a36Sopenharmony_ci goto err_disable_clk; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Allegedly this turns the power save mode off */ 34362306a36Sopenharmony_ci writel(0UL, ebi2_xmem + EBI2_XMEM_CFG); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Disable all chipselects */ 34662306a36Sopenharmony_ci val = readl(ebi2_base); 34762306a36Sopenharmony_ci val &= ~EBI2_CSN_MASK; 34862306a36Sopenharmony_ci writel(val, ebi2_base); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Walk over the child nodes and see what chipselects we use */ 35162306a36Sopenharmony_ci for_each_available_child_of_node(np, child) { 35262306a36Sopenharmony_ci u32 csindex; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Figure out the chipselect */ 35562306a36Sopenharmony_ci ret = of_property_read_u32(child, "reg", &csindex); 35662306a36Sopenharmony_ci if (ret) { 35762306a36Sopenharmony_ci of_node_put(child); 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (csindex > 5) { 36262306a36Sopenharmony_ci dev_err(dev, 36362306a36Sopenharmony_ci "invalid chipselect %u, we only support 0-5\n", 36462306a36Sopenharmony_ci csindex); 36562306a36Sopenharmony_ci continue; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci qcom_ebi2_setup_chipselect(child, 36962306a36Sopenharmony_ci dev, 37062306a36Sopenharmony_ci ebi2_base, 37162306a36Sopenharmony_ci ebi2_xmem, 37262306a36Sopenharmony_ci csindex); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* We have at least one child */ 37562306a36Sopenharmony_ci have_children = true; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (have_children) 37962306a36Sopenharmony_ci return of_platform_default_populate(np, NULL, dev); 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cierr_disable_clk: 38362306a36Sopenharmony_ci clk_disable_unprepare(ebi2clk); 38462306a36Sopenharmony_cierr_disable_2x_clk: 38562306a36Sopenharmony_ci clk_disable_unprepare(ebi2xclk); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return ret; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic const struct of_device_id qcom_ebi2_of_match[] = { 39162306a36Sopenharmony_ci { .compatible = "qcom,msm8660-ebi2", }, 39262306a36Sopenharmony_ci { .compatible = "qcom,apq8060-ebi2", }, 39362306a36Sopenharmony_ci { } 39462306a36Sopenharmony_ci}; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic struct platform_driver qcom_ebi2_driver = { 39762306a36Sopenharmony_ci .probe = qcom_ebi2_probe, 39862306a36Sopenharmony_ci .driver = { 39962306a36Sopenharmony_ci .name = "qcom-ebi2", 40062306a36Sopenharmony_ci .of_match_table = qcom_ebi2_of_match, 40162306a36Sopenharmony_ci }, 40262306a36Sopenharmony_ci}; 40362306a36Sopenharmony_cimodule_platform_driver(qcom_ebi2_driver); 40462306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 40562306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm EBI2 driver"); 406