18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Qualcomm External Bus Interface 2 (EBI2) driver 48c2ecf20Sopenharmony_ci * an older version of the Qualcomm Parallel Interface Controller (QPIC) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Linaro Ltd. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * See the device tree bindings for this block for more details on the 118c2ecf20Sopenharmony_ci * hardware. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/bitops.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * CS0, CS1, CS4 and CS5 are two bits wide, CS2 and CS3 are one bit. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#define EBI2_CS0_ENABLE_MASK BIT(0)|BIT(1) 298c2ecf20Sopenharmony_ci#define EBI2_CS1_ENABLE_MASK BIT(2)|BIT(3) 308c2ecf20Sopenharmony_ci#define EBI2_CS2_ENABLE_MASK BIT(4) 318c2ecf20Sopenharmony_ci#define EBI2_CS3_ENABLE_MASK BIT(5) 328c2ecf20Sopenharmony_ci#define EBI2_CS4_ENABLE_MASK BIT(6)|BIT(7) 338c2ecf20Sopenharmony_ci#define EBI2_CS5_ENABLE_MASK BIT(8)|BIT(9) 348c2ecf20Sopenharmony_ci#define EBI2_CSN_MASK GENMASK(9, 0) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define EBI2_XMEM_CFG 0x0000 /* Power management etc */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * SLOW CSn CFG 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Bits 31-28: RECOVERY recovery cycles (0 = 1, 1 = 2 etc) this is the time the 428c2ecf20Sopenharmony_ci * memory continues to drive the data bus after OE is de-asserted. 438c2ecf20Sopenharmony_ci * Inserted when reading one CS and switching to another CS or read 448c2ecf20Sopenharmony_ci * followed by write on the same CS. Valid values 0 thru 15. 458c2ecf20Sopenharmony_ci * Bits 27-24: WR_HOLD write hold cycles, these are extra cycles inserted after 468c2ecf20Sopenharmony_ci * every write minimum 1. The data out is driven from the time WE is 478c2ecf20Sopenharmony_ci * asserted until CS is asserted. With a hold of 1, the CS stays 488c2ecf20Sopenharmony_ci * active for 1 extra cycle etc. Valid values 0 thru 15. 498c2ecf20Sopenharmony_ci * Bits 23-16: WR_DELTA initial latency for write cycles inserted for the first 508c2ecf20Sopenharmony_ci * write to a page or burst memory 518c2ecf20Sopenharmony_ci * Bits 15-8: RD_DELTA initial latency for read cycles inserted for the first 528c2ecf20Sopenharmony_ci * read to a page or burst memory 538c2ecf20Sopenharmony_ci * Bits 7-4: WR_WAIT number of wait cycles for every write access, 0=1 cycle 548c2ecf20Sopenharmony_ci * so 1 thru 16 cycles. 558c2ecf20Sopenharmony_ci * Bits 3-0: RD_WAIT number of wait cycles for every read access, 0=1 cycle 568c2ecf20Sopenharmony_ci * so 1 thru 16 cycles. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS0_SLOW_CFG 0x0008 598c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS1_SLOW_CFG 0x000C 608c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS2_SLOW_CFG 0x0010 618c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS3_SLOW_CFG 0x0014 628c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS4_SLOW_CFG 0x0018 638c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS5_SLOW_CFG 0x001C 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define EBI2_XMEM_RECOVERY_SHIFT 28 668c2ecf20Sopenharmony_ci#define EBI2_XMEM_WR_HOLD_SHIFT 24 678c2ecf20Sopenharmony_ci#define EBI2_XMEM_WR_DELTA_SHIFT 16 688c2ecf20Sopenharmony_ci#define EBI2_XMEM_RD_DELTA_SHIFT 8 698c2ecf20Sopenharmony_ci#define EBI2_XMEM_WR_WAIT_SHIFT 4 708c2ecf20Sopenharmony_ci#define EBI2_XMEM_RD_WAIT_SHIFT 0 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * FAST CSn CFG 748c2ecf20Sopenharmony_ci * Bits 31-28: ? 758c2ecf20Sopenharmony_ci * Bits 27-24: RD_HOLD: the length in cycles of the first segment of a read 768c2ecf20Sopenharmony_ci * transfer. For a single read trandfer this will be the time 778c2ecf20Sopenharmony_ci * from CS assertion to OE assertion. 788c2ecf20Sopenharmony_ci * Bits 18-24: ? 798c2ecf20Sopenharmony_ci * Bits 17-16: ADV_OE_RECOVERY, the number of cycles elapsed before an OE 808c2ecf20Sopenharmony_ci * assertion, with respect to the cycle where ADV is asserted. 818c2ecf20Sopenharmony_ci * 2 means 2 cycles between ADV and OE. Values 0, 1, 2 or 3. 828c2ecf20Sopenharmony_ci * Bits 5: ADDR_HOLD_ENA, The address is held for an extra cycle to meet 838c2ecf20Sopenharmony_ci * hold time requirements with ADV assertion. 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * The manual mentions "write precharge cycles" and "precharge cycles". 868c2ecf20Sopenharmony_ci * We have not been able to figure out which bit fields these correspond to 878c2ecf20Sopenharmony_ci * in the hardware, or what valid values exist. The current hypothesis is that 888c2ecf20Sopenharmony_ci * this is something just used on the FAST chip selects. There is also a "byte 898c2ecf20Sopenharmony_ci * device enable" flag somewhere for 8bit memories. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS0_FAST_CFG 0x0028 928c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS1_FAST_CFG 0x002C 938c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS2_FAST_CFG 0x0030 948c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS3_FAST_CFG 0x0034 958c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS4_FAST_CFG 0x0038 968c2ecf20Sopenharmony_ci#define EBI2_XMEM_CS5_FAST_CFG 0x003C 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define EBI2_XMEM_RD_HOLD_SHIFT 24 998c2ecf20Sopenharmony_ci#define EBI2_XMEM_ADV_OE_RECOVERY_SHIFT 16 1008c2ecf20Sopenharmony_ci#define EBI2_XMEM_ADDR_HOLD_ENA_SHIFT 5 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/** 1038c2ecf20Sopenharmony_ci * struct cs_data - struct with info on a chipselect setting 1048c2ecf20Sopenharmony_ci * @enable_mask: mask to enable the chipselect in the EBI2 config 1058c2ecf20Sopenharmony_ci * @slow_cfg0: offset to XMEMC slow CS config 1068c2ecf20Sopenharmony_ci * @fast_cfg1: offset to XMEMC fast CS config 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistruct cs_data { 1098c2ecf20Sopenharmony_ci u32 enable_mask; 1108c2ecf20Sopenharmony_ci u16 slow_cfg; 1118c2ecf20Sopenharmony_ci u16 fast_cfg; 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct cs_data cs_info[] = { 1158c2ecf20Sopenharmony_ci { 1168c2ecf20Sopenharmony_ci /* CS0 */ 1178c2ecf20Sopenharmony_ci .enable_mask = EBI2_CS0_ENABLE_MASK, 1188c2ecf20Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS0_SLOW_CFG, 1198c2ecf20Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS0_FAST_CFG, 1208c2ecf20Sopenharmony_ci }, 1218c2ecf20Sopenharmony_ci { 1228c2ecf20Sopenharmony_ci /* CS1 */ 1238c2ecf20Sopenharmony_ci .enable_mask = EBI2_CS1_ENABLE_MASK, 1248c2ecf20Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS1_SLOW_CFG, 1258c2ecf20Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS1_FAST_CFG, 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci { 1288c2ecf20Sopenharmony_ci /* CS2 */ 1298c2ecf20Sopenharmony_ci .enable_mask = EBI2_CS2_ENABLE_MASK, 1308c2ecf20Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS2_SLOW_CFG, 1318c2ecf20Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS2_FAST_CFG, 1328c2ecf20Sopenharmony_ci }, 1338c2ecf20Sopenharmony_ci { 1348c2ecf20Sopenharmony_ci /* CS3 */ 1358c2ecf20Sopenharmony_ci .enable_mask = EBI2_CS3_ENABLE_MASK, 1368c2ecf20Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS3_SLOW_CFG, 1378c2ecf20Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS3_FAST_CFG, 1388c2ecf20Sopenharmony_ci }, 1398c2ecf20Sopenharmony_ci { 1408c2ecf20Sopenharmony_ci /* CS4 */ 1418c2ecf20Sopenharmony_ci .enable_mask = EBI2_CS4_ENABLE_MASK, 1428c2ecf20Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS4_SLOW_CFG, 1438c2ecf20Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS4_FAST_CFG, 1448c2ecf20Sopenharmony_ci }, 1458c2ecf20Sopenharmony_ci { 1468c2ecf20Sopenharmony_ci /* CS5 */ 1478c2ecf20Sopenharmony_ci .enable_mask = EBI2_CS5_ENABLE_MASK, 1488c2ecf20Sopenharmony_ci .slow_cfg = EBI2_XMEM_CS5_SLOW_CFG, 1498c2ecf20Sopenharmony_ci .fast_cfg = EBI2_XMEM_CS5_FAST_CFG, 1508c2ecf20Sopenharmony_ci }, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * struct ebi2_xmem_prop - describes an XMEM config property 1558c2ecf20Sopenharmony_ci * @prop: the device tree binding name 1568c2ecf20Sopenharmony_ci * @max: maximum value for the property 1578c2ecf20Sopenharmony_ci * @slowreg: true if this property is in the SLOW CS config register 1588c2ecf20Sopenharmony_ci * else it is assumed to be in the FAST config register 1598c2ecf20Sopenharmony_ci * @shift: the bit field start in the SLOW or FAST register for this 1608c2ecf20Sopenharmony_ci * property 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_cistruct ebi2_xmem_prop { 1638c2ecf20Sopenharmony_ci const char *prop; 1648c2ecf20Sopenharmony_ci u32 max; 1658c2ecf20Sopenharmony_ci bool slowreg; 1668c2ecf20Sopenharmony_ci u16 shift; 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const struct ebi2_xmem_prop xmem_props[] = { 1708c2ecf20Sopenharmony_ci { 1718c2ecf20Sopenharmony_ci .prop = "qcom,xmem-recovery-cycles", 1728c2ecf20Sopenharmony_ci .max = 15, 1738c2ecf20Sopenharmony_ci .slowreg = true, 1748c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_RECOVERY_SHIFT, 1758c2ecf20Sopenharmony_ci }, 1768c2ecf20Sopenharmony_ci { 1778c2ecf20Sopenharmony_ci .prop = "qcom,xmem-write-hold-cycles", 1788c2ecf20Sopenharmony_ci .max = 15, 1798c2ecf20Sopenharmony_ci .slowreg = true, 1808c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_WR_HOLD_SHIFT, 1818c2ecf20Sopenharmony_ci }, 1828c2ecf20Sopenharmony_ci { 1838c2ecf20Sopenharmony_ci .prop = "qcom,xmem-write-delta-cycles", 1848c2ecf20Sopenharmony_ci .max = 255, 1858c2ecf20Sopenharmony_ci .slowreg = true, 1868c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_WR_DELTA_SHIFT, 1878c2ecf20Sopenharmony_ci }, 1888c2ecf20Sopenharmony_ci { 1898c2ecf20Sopenharmony_ci .prop = "qcom,xmem-read-delta-cycles", 1908c2ecf20Sopenharmony_ci .max = 255, 1918c2ecf20Sopenharmony_ci .slowreg = true, 1928c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_RD_DELTA_SHIFT, 1938c2ecf20Sopenharmony_ci }, 1948c2ecf20Sopenharmony_ci { 1958c2ecf20Sopenharmony_ci .prop = "qcom,xmem-write-wait-cycles", 1968c2ecf20Sopenharmony_ci .max = 15, 1978c2ecf20Sopenharmony_ci .slowreg = true, 1988c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_WR_WAIT_SHIFT, 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci { 2018c2ecf20Sopenharmony_ci .prop = "qcom,xmem-read-wait-cycles", 2028c2ecf20Sopenharmony_ci .max = 15, 2038c2ecf20Sopenharmony_ci .slowreg = true, 2048c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_RD_WAIT_SHIFT, 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci { 2078c2ecf20Sopenharmony_ci .prop = "qcom,xmem-address-hold-enable", 2088c2ecf20Sopenharmony_ci .max = 1, /* boolean prop */ 2098c2ecf20Sopenharmony_ci .slowreg = false, 2108c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_ADDR_HOLD_ENA_SHIFT, 2118c2ecf20Sopenharmony_ci }, 2128c2ecf20Sopenharmony_ci { 2138c2ecf20Sopenharmony_ci .prop = "qcom,xmem-adv-to-oe-recovery-cycles", 2148c2ecf20Sopenharmony_ci .max = 3, 2158c2ecf20Sopenharmony_ci .slowreg = false, 2168c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_ADV_OE_RECOVERY_SHIFT, 2178c2ecf20Sopenharmony_ci }, 2188c2ecf20Sopenharmony_ci { 2198c2ecf20Sopenharmony_ci .prop = "qcom,xmem-read-hold-cycles", 2208c2ecf20Sopenharmony_ci .max = 15, 2218c2ecf20Sopenharmony_ci .slowreg = false, 2228c2ecf20Sopenharmony_ci .shift = EBI2_XMEM_RD_HOLD_SHIFT, 2238c2ecf20Sopenharmony_ci }, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void qcom_ebi2_setup_chipselect(struct device_node *np, 2278c2ecf20Sopenharmony_ci struct device *dev, 2288c2ecf20Sopenharmony_ci void __iomem *ebi2_base, 2298c2ecf20Sopenharmony_ci void __iomem *ebi2_xmem, 2308c2ecf20Sopenharmony_ci u32 csindex) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci const struct cs_data *csd; 2338c2ecf20Sopenharmony_ci u32 slowcfg, fastcfg; 2348c2ecf20Sopenharmony_ci u32 val; 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci int i; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci csd = &cs_info[csindex]; 2398c2ecf20Sopenharmony_ci val = readl(ebi2_base); 2408c2ecf20Sopenharmony_ci val |= csd->enable_mask; 2418c2ecf20Sopenharmony_ci writel(val, ebi2_base); 2428c2ecf20Sopenharmony_ci dev_dbg(dev, "enabled CS%u\n", csindex); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Next set up the XMEMC */ 2458c2ecf20Sopenharmony_ci slowcfg = 0; 2468c2ecf20Sopenharmony_ci fastcfg = 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xmem_props); i++) { 2498c2ecf20Sopenharmony_ci const struct ebi2_xmem_prop *xp = &xmem_props[i]; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* All are regular u32 values */ 2528c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, xp->prop, &val); 2538c2ecf20Sopenharmony_ci if (ret) { 2548c2ecf20Sopenharmony_ci dev_dbg(dev, "could not read %s for CS%d\n", 2558c2ecf20Sopenharmony_ci xp->prop, csindex); 2568c2ecf20Sopenharmony_ci continue; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* First check boolean props */ 2608c2ecf20Sopenharmony_ci if (xp->max == 1 && val) { 2618c2ecf20Sopenharmony_ci if (xp->slowreg) 2628c2ecf20Sopenharmony_ci slowcfg |= BIT(xp->shift); 2638c2ecf20Sopenharmony_ci else 2648c2ecf20Sopenharmony_ci fastcfg |= BIT(xp->shift); 2658c2ecf20Sopenharmony_ci dev_dbg(dev, "set %s flag\n", xp->prop); 2668c2ecf20Sopenharmony_ci continue; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* We're dealing with an u32 */ 2708c2ecf20Sopenharmony_ci if (val > xp->max) { 2718c2ecf20Sopenharmony_ci dev_err(dev, 2728c2ecf20Sopenharmony_ci "too high value for %s: %u, capped at %u\n", 2738c2ecf20Sopenharmony_ci xp->prop, val, xp->max); 2748c2ecf20Sopenharmony_ci val = xp->max; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci if (xp->slowreg) 2778c2ecf20Sopenharmony_ci slowcfg |= (val << xp->shift); 2788c2ecf20Sopenharmony_ci else 2798c2ecf20Sopenharmony_ci fastcfg |= (val << xp->shift); 2808c2ecf20Sopenharmony_ci dev_dbg(dev, "set %s to %u\n", xp->prop, val); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci dev_info(dev, "CS%u: SLOW CFG 0x%08x, FAST CFG 0x%08x\n", 2848c2ecf20Sopenharmony_ci csindex, slowcfg, fastcfg); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (slowcfg) 2878c2ecf20Sopenharmony_ci writel(slowcfg, ebi2_xmem + csd->slow_cfg); 2888c2ecf20Sopenharmony_ci if (fastcfg) 2898c2ecf20Sopenharmony_ci writel(fastcfg, ebi2_xmem + csd->fast_cfg); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int qcom_ebi2_probe(struct platform_device *pdev) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 2958c2ecf20Sopenharmony_ci struct device_node *child; 2968c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2978c2ecf20Sopenharmony_ci struct resource *res; 2988c2ecf20Sopenharmony_ci void __iomem *ebi2_base; 2998c2ecf20Sopenharmony_ci void __iomem *ebi2_xmem; 3008c2ecf20Sopenharmony_ci struct clk *ebi2xclk; 3018c2ecf20Sopenharmony_ci struct clk *ebi2clk; 3028c2ecf20Sopenharmony_ci bool have_children = false; 3038c2ecf20Sopenharmony_ci u32 val; 3048c2ecf20Sopenharmony_ci int ret; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci ebi2xclk = devm_clk_get(dev, "ebi2x"); 3078c2ecf20Sopenharmony_ci if (IS_ERR(ebi2xclk)) 3088c2ecf20Sopenharmony_ci return PTR_ERR(ebi2xclk); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ebi2xclk); 3118c2ecf20Sopenharmony_ci if (ret) { 3128c2ecf20Sopenharmony_ci dev_err(dev, "could not enable EBI2X clk (%d)\n", ret); 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ebi2clk = devm_clk_get(dev, "ebi2"); 3178c2ecf20Sopenharmony_ci if (IS_ERR(ebi2clk)) { 3188c2ecf20Sopenharmony_ci ret = PTR_ERR(ebi2clk); 3198c2ecf20Sopenharmony_ci goto err_disable_2x_clk; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ebi2clk); 3238c2ecf20Sopenharmony_ci if (ret) { 3248c2ecf20Sopenharmony_ci dev_err(dev, "could not enable EBI2 clk\n"); 3258c2ecf20Sopenharmony_ci goto err_disable_2x_clk; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3298c2ecf20Sopenharmony_ci ebi2_base = devm_ioremap_resource(dev, res); 3308c2ecf20Sopenharmony_ci if (IS_ERR(ebi2_base)) { 3318c2ecf20Sopenharmony_ci ret = PTR_ERR(ebi2_base); 3328c2ecf20Sopenharmony_ci goto err_disable_clk; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 3368c2ecf20Sopenharmony_ci ebi2_xmem = devm_ioremap_resource(dev, res); 3378c2ecf20Sopenharmony_ci if (IS_ERR(ebi2_xmem)) { 3388c2ecf20Sopenharmony_ci ret = PTR_ERR(ebi2_xmem); 3398c2ecf20Sopenharmony_ci goto err_disable_clk; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Allegedly this turns the power save mode off */ 3438c2ecf20Sopenharmony_ci writel(0UL, ebi2_xmem + EBI2_XMEM_CFG); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Disable all chipselects */ 3468c2ecf20Sopenharmony_ci val = readl(ebi2_base); 3478c2ecf20Sopenharmony_ci val &= ~EBI2_CSN_MASK; 3488c2ecf20Sopenharmony_ci writel(val, ebi2_base); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Walk over the child nodes and see what chipselects we use */ 3518c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, child) { 3528c2ecf20Sopenharmony_ci u32 csindex; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Figure out the chipselect */ 3558c2ecf20Sopenharmony_ci ret = of_property_read_u32(child, "reg", &csindex); 3568c2ecf20Sopenharmony_ci if (ret) { 3578c2ecf20Sopenharmony_ci of_node_put(child); 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (csindex > 5) { 3628c2ecf20Sopenharmony_ci dev_err(dev, 3638c2ecf20Sopenharmony_ci "invalid chipselect %u, we only support 0-5\n", 3648c2ecf20Sopenharmony_ci csindex); 3658c2ecf20Sopenharmony_ci continue; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci qcom_ebi2_setup_chipselect(child, 3698c2ecf20Sopenharmony_ci dev, 3708c2ecf20Sopenharmony_ci ebi2_base, 3718c2ecf20Sopenharmony_ci ebi2_xmem, 3728c2ecf20Sopenharmony_ci csindex); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* We have at least one child */ 3758c2ecf20Sopenharmony_ci have_children = true; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (have_children) 3798c2ecf20Sopenharmony_ci return of_platform_default_populate(np, NULL, dev); 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cierr_disable_clk: 3838c2ecf20Sopenharmony_ci clk_disable_unprepare(ebi2clk); 3848c2ecf20Sopenharmony_cierr_disable_2x_clk: 3858c2ecf20Sopenharmony_ci clk_disable_unprepare(ebi2xclk); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_ebi2_of_match[] = { 3918c2ecf20Sopenharmony_ci { .compatible = "qcom,msm8660-ebi2", }, 3928c2ecf20Sopenharmony_ci { .compatible = "qcom,apq8060-ebi2", }, 3938c2ecf20Sopenharmony_ci { } 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic struct platform_driver qcom_ebi2_driver = { 3978c2ecf20Sopenharmony_ci .probe = qcom_ebi2_probe, 3988c2ecf20Sopenharmony_ci .driver = { 3998c2ecf20Sopenharmony_ci .name = "qcom-ebi2", 4008c2ecf20Sopenharmony_ci .of_match_table = qcom_ebi2_of_match, 4018c2ecf20Sopenharmony_ci }, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_cimodule_platform_driver(qcom_ebi2_driver); 4048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 4058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm EBI2 driver"); 4068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 407