14af34b57SMaxime Ripard /* 24af34b57SMaxime Ripard * Allwinner SoCs SRAM Controller Driver 34af34b57SMaxime Ripard * 44af34b57SMaxime Ripard * Copyright (C) 2015 Maxime Ripard 54af34b57SMaxime Ripard * 64af34b57SMaxime Ripard * Author: Maxime Ripard <maxime.ripard@free-electrons.com> 74af34b57SMaxime Ripard * 84af34b57SMaxime Ripard * This file is licensed under the terms of the GNU General Public 94af34b57SMaxime Ripard * License version 2. This program is licensed "as is" without any 104af34b57SMaxime Ripard * warranty of any kind, whether express or implied. 114af34b57SMaxime Ripard */ 124af34b57SMaxime Ripard 134af34b57SMaxime Ripard #include <linux/debugfs.h> 144af34b57SMaxime Ripard #include <linux/io.h> 154af34b57SMaxime Ripard #include <linux/module.h> 164af34b57SMaxime Ripard #include <linux/of.h> 174af34b57SMaxime Ripard #include <linux/of_address.h> 184af34b57SMaxime Ripard #include <linux/of_device.h> 194af34b57SMaxime Ripard #include <linux/platform_device.h> 204af34b57SMaxime Ripard 214af34b57SMaxime Ripard #include <linux/soc/sunxi/sunxi_sram.h> 224af34b57SMaxime Ripard 234af34b57SMaxime Ripard struct sunxi_sram_func { 244af34b57SMaxime Ripard char *func; 254af34b57SMaxime Ripard u8 val; 268fed2ce9SIcenowy Zheng u32 reg_val; 274af34b57SMaxime Ripard }; 284af34b57SMaxime Ripard 294af34b57SMaxime Ripard struct sunxi_sram_data { 304af34b57SMaxime Ripard char *name; 314af34b57SMaxime Ripard u8 reg; 324af34b57SMaxime Ripard u8 offset; 334af34b57SMaxime Ripard u8 width; 344af34b57SMaxime Ripard struct sunxi_sram_func *func; 354af34b57SMaxime Ripard struct list_head list; 364af34b57SMaxime Ripard }; 374af34b57SMaxime Ripard 384af34b57SMaxime Ripard struct sunxi_sram_desc { 394af34b57SMaxime Ripard struct sunxi_sram_data data; 404af34b57SMaxime Ripard bool claimed; 414af34b57SMaxime Ripard }; 424af34b57SMaxime Ripard 438fed2ce9SIcenowy Zheng #define SUNXI_SRAM_MAP(_reg_val, _val, _func) \ 444af34b57SMaxime Ripard { \ 454af34b57SMaxime Ripard .func = _func, \ 464af34b57SMaxime Ripard .val = _val, \ 478fed2ce9SIcenowy Zheng .reg_val = _reg_val, \ 484af34b57SMaxime Ripard } 494af34b57SMaxime Ripard 504af34b57SMaxime Ripard #define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...) \ 514af34b57SMaxime Ripard { \ 524af34b57SMaxime Ripard .name = _name, \ 534af34b57SMaxime Ripard .reg = _reg, \ 544af34b57SMaxime Ripard .offset = _off, \ 554af34b57SMaxime Ripard .width = _width, \ 564af34b57SMaxime Ripard .func = (struct sunxi_sram_func[]){ \ 574af34b57SMaxime Ripard __VA_ARGS__, { } }, \ 584af34b57SMaxime Ripard } 594af34b57SMaxime Ripard 604af34b57SMaxime Ripard static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = { 614af34b57SMaxime Ripard .data = SUNXI_SRAM_DATA("A3-A4", 0x4, 0x4, 2, 628fed2ce9SIcenowy Zheng SUNXI_SRAM_MAP(0, 0, "cpu"), 638fed2ce9SIcenowy Zheng SUNXI_SRAM_MAP(1, 1, "emac")), 644af34b57SMaxime Ripard }; 654af34b57SMaxime Ripard 664af34b57SMaxime Ripard static struct sunxi_sram_desc sun4i_a10_sram_d = { 674af34b57SMaxime Ripard .data = SUNXI_SRAM_DATA("D", 0x4, 0x0, 1, 688fed2ce9SIcenowy Zheng SUNXI_SRAM_MAP(0, 0, "cpu"), 698fed2ce9SIcenowy Zheng SUNXI_SRAM_MAP(1, 1, "usb-otg")), 704af34b57SMaxime Ripard }; 714af34b57SMaxime Ripard 724af34b57SMaxime Ripard static const struct of_device_id sunxi_sram_dt_ids[] = { 734af34b57SMaxime Ripard { 744af34b57SMaxime Ripard .compatible = "allwinner,sun4i-a10-sram-a3-a4", 754af34b57SMaxime Ripard .data = &sun4i_a10_sram_a3_a4.data, 764af34b57SMaxime Ripard }, 774af34b57SMaxime Ripard { 784af34b57SMaxime Ripard .compatible = "allwinner,sun4i-a10-sram-d", 794af34b57SMaxime Ripard .data = &sun4i_a10_sram_d.data, 804af34b57SMaxime Ripard }, 814af34b57SMaxime Ripard {} 824af34b57SMaxime Ripard }; 834af34b57SMaxime Ripard 844af34b57SMaxime Ripard static struct device *sram_dev; 854af34b57SMaxime Ripard static LIST_HEAD(claimed_sram); 864af34b57SMaxime Ripard static DEFINE_SPINLOCK(sram_lock); 874af34b57SMaxime Ripard static void __iomem *base; 884af34b57SMaxime Ripard 894af34b57SMaxime Ripard static int sunxi_sram_show(struct seq_file *s, void *data) 904af34b57SMaxime Ripard { 914af34b57SMaxime Ripard struct device_node *sram_node, *section_node; 924af34b57SMaxime Ripard const struct sunxi_sram_data *sram_data; 934af34b57SMaxime Ripard const struct of_device_id *match; 944af34b57SMaxime Ripard struct sunxi_sram_func *func; 954af34b57SMaxime Ripard const __be32 *sram_addr_p, *section_addr_p; 964af34b57SMaxime Ripard u32 val; 974af34b57SMaxime Ripard 984af34b57SMaxime Ripard seq_puts(s, "Allwinner sunXi SRAM\n"); 994af34b57SMaxime Ripard seq_puts(s, "--------------------\n\n"); 1004af34b57SMaxime Ripard 1014af34b57SMaxime Ripard for_each_child_of_node(sram_dev->of_node, sram_node) { 1024af34b57SMaxime Ripard sram_addr_p = of_get_address(sram_node, 0, NULL, NULL); 1034af34b57SMaxime Ripard 1044af34b57SMaxime Ripard seq_printf(s, "sram@%08x\n", 1054af34b57SMaxime Ripard be32_to_cpu(*sram_addr_p)); 1064af34b57SMaxime Ripard 1074af34b57SMaxime Ripard for_each_child_of_node(sram_node, section_node) { 1084af34b57SMaxime Ripard match = of_match_node(sunxi_sram_dt_ids, section_node); 1094af34b57SMaxime Ripard if (!match) 1104af34b57SMaxime Ripard continue; 1114af34b57SMaxime Ripard sram_data = match->data; 1124af34b57SMaxime Ripard 1134af34b57SMaxime Ripard section_addr_p = of_get_address(section_node, 0, 1144af34b57SMaxime Ripard NULL, NULL); 1154af34b57SMaxime Ripard 1164af34b57SMaxime Ripard seq_printf(s, "\tsection@%04x\t(%s)\n", 1174af34b57SMaxime Ripard be32_to_cpu(*section_addr_p), 1184af34b57SMaxime Ripard sram_data->name); 1194af34b57SMaxime Ripard 1204af34b57SMaxime Ripard val = readl(base + sram_data->reg); 1214af34b57SMaxime Ripard val >>= sram_data->offset; 122febe6569SJens Kuske val &= GENMASK(sram_data->width - 1, 0); 1234af34b57SMaxime Ripard 1244af34b57SMaxime Ripard for (func = sram_data->func; func->func; func++) { 1254af34b57SMaxime Ripard seq_printf(s, "\t\t%s%c\n", func->func, 1268fed2ce9SIcenowy Zheng func->reg_val == val ? 1278fed2ce9SIcenowy Zheng '*' : ' '); 1284af34b57SMaxime Ripard } 1294af34b57SMaxime Ripard } 1304af34b57SMaxime Ripard 1314af34b57SMaxime Ripard seq_puts(s, "\n"); 1324af34b57SMaxime Ripard } 1334af34b57SMaxime Ripard 1344af34b57SMaxime Ripard return 0; 1354af34b57SMaxime Ripard } 1364af34b57SMaxime Ripard 1374af34b57SMaxime Ripard static int sunxi_sram_open(struct inode *inode, struct file *file) 1384af34b57SMaxime Ripard { 1394af34b57SMaxime Ripard return single_open(file, sunxi_sram_show, inode->i_private); 1404af34b57SMaxime Ripard } 1414af34b57SMaxime Ripard 1424af34b57SMaxime Ripard static const struct file_operations sunxi_sram_fops = { 1434af34b57SMaxime Ripard .open = sunxi_sram_open, 1444af34b57SMaxime Ripard .read = seq_read, 1454af34b57SMaxime Ripard .llseek = seq_lseek, 1464af34b57SMaxime Ripard .release = single_release, 1474af34b57SMaxime Ripard }; 1484af34b57SMaxime Ripard 1494af34b57SMaxime Ripard static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data) 1504af34b57SMaxime Ripard { 1514af34b57SMaxime Ripard return container_of(data, struct sunxi_sram_desc, data); 1524af34b57SMaxime Ripard } 1534af34b57SMaxime Ripard 1544af34b57SMaxime Ripard static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node, 1558fed2ce9SIcenowy Zheng unsigned int *reg_value) 1564af34b57SMaxime Ripard { 1574af34b57SMaxime Ripard const struct of_device_id *match; 1588fed2ce9SIcenowy Zheng const struct sunxi_sram_data *data; 1598fed2ce9SIcenowy Zheng struct sunxi_sram_func *func; 1604af34b57SMaxime Ripard struct of_phandle_args args; 1618fed2ce9SIcenowy Zheng u8 val; 1624af34b57SMaxime Ripard int ret; 1634af34b57SMaxime Ripard 1644af34b57SMaxime Ripard ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0, 1654af34b57SMaxime Ripard &args); 1664af34b57SMaxime Ripard if (ret) 1674af34b57SMaxime Ripard return ERR_PTR(ret); 1684af34b57SMaxime Ripard 1694af34b57SMaxime Ripard if (!of_device_is_available(args.np)) { 1704af34b57SMaxime Ripard ret = -EBUSY; 1714af34b57SMaxime Ripard goto err; 1724af34b57SMaxime Ripard } 1734af34b57SMaxime Ripard 1748fed2ce9SIcenowy Zheng val = args.args[0]; 1754af34b57SMaxime Ripard 1764af34b57SMaxime Ripard match = of_match_node(sunxi_sram_dt_ids, args.np); 1774af34b57SMaxime Ripard if (!match) { 1784af34b57SMaxime Ripard ret = -EINVAL; 1794af34b57SMaxime Ripard goto err; 1804af34b57SMaxime Ripard } 1814af34b57SMaxime Ripard 1828fed2ce9SIcenowy Zheng data = match->data; 1838fed2ce9SIcenowy Zheng if (!data) { 1848fed2ce9SIcenowy Zheng ret = -EINVAL; 1858fed2ce9SIcenowy Zheng goto err; 1868fed2ce9SIcenowy Zheng }; 1878fed2ce9SIcenowy Zheng 1888fed2ce9SIcenowy Zheng for (func = data->func; func->func; func++) { 1898fed2ce9SIcenowy Zheng if (val == func->val) { 1908fed2ce9SIcenowy Zheng if (reg_value) 1918fed2ce9SIcenowy Zheng *reg_value = func->reg_val; 1928fed2ce9SIcenowy Zheng 1938fed2ce9SIcenowy Zheng break; 1948fed2ce9SIcenowy Zheng } 1958fed2ce9SIcenowy Zheng } 1968fed2ce9SIcenowy Zheng 1978fed2ce9SIcenowy Zheng if (!func->func) { 1988fed2ce9SIcenowy Zheng ret = -EINVAL; 1998fed2ce9SIcenowy Zheng goto err; 2008fed2ce9SIcenowy Zheng } 2018fed2ce9SIcenowy Zheng 2024af34b57SMaxime Ripard of_node_put(args.np); 2034af34b57SMaxime Ripard return match->data; 2044af34b57SMaxime Ripard 2054af34b57SMaxime Ripard err: 2064af34b57SMaxime Ripard of_node_put(args.np); 2074af34b57SMaxime Ripard return ERR_PTR(ret); 2084af34b57SMaxime Ripard } 2094af34b57SMaxime Ripard 2104af34b57SMaxime Ripard int sunxi_sram_claim(struct device *dev) 2114af34b57SMaxime Ripard { 2124af34b57SMaxime Ripard const struct sunxi_sram_data *sram_data; 2134af34b57SMaxime Ripard struct sunxi_sram_desc *sram_desc; 2144af34b57SMaxime Ripard unsigned int device; 2154af34b57SMaxime Ripard u32 val, mask; 2164af34b57SMaxime Ripard 2174af34b57SMaxime Ripard if (IS_ERR(base)) 2182262a65fSIcenowy Zheng return PTR_ERR(base); 2192262a65fSIcenowy Zheng 2202262a65fSIcenowy Zheng if (!base) 2214af34b57SMaxime Ripard return -EPROBE_DEFER; 2224af34b57SMaxime Ripard 2234af34b57SMaxime Ripard if (!dev || !dev->of_node) 2244af34b57SMaxime Ripard return -EINVAL; 2254af34b57SMaxime Ripard 2264af34b57SMaxime Ripard sram_data = sunxi_sram_of_parse(dev->of_node, &device); 2274af34b57SMaxime Ripard if (IS_ERR(sram_data)) 2284af34b57SMaxime Ripard return PTR_ERR(sram_data); 2294af34b57SMaxime Ripard 2304af34b57SMaxime Ripard sram_desc = to_sram_desc(sram_data); 2314af34b57SMaxime Ripard 2324af34b57SMaxime Ripard spin_lock(&sram_lock); 2334af34b57SMaxime Ripard 2344af34b57SMaxime Ripard if (sram_desc->claimed) { 2354af34b57SMaxime Ripard spin_unlock(&sram_lock); 2364af34b57SMaxime Ripard return -EBUSY; 2374af34b57SMaxime Ripard } 2384af34b57SMaxime Ripard 239febe6569SJens Kuske mask = GENMASK(sram_data->offset + sram_data->width - 1, 240febe6569SJens Kuske sram_data->offset); 2414af34b57SMaxime Ripard val = readl(base + sram_data->reg); 2424af34b57SMaxime Ripard val &= ~mask; 2434af34b57SMaxime Ripard writel(val | ((device << sram_data->offset) & mask), 2444af34b57SMaxime Ripard base + sram_data->reg); 2454af34b57SMaxime Ripard 2464af34b57SMaxime Ripard spin_unlock(&sram_lock); 2474af34b57SMaxime Ripard 2484af34b57SMaxime Ripard return 0; 2494af34b57SMaxime Ripard } 2504af34b57SMaxime Ripard EXPORT_SYMBOL(sunxi_sram_claim); 2514af34b57SMaxime Ripard 2524af34b57SMaxime Ripard int sunxi_sram_release(struct device *dev) 2534af34b57SMaxime Ripard { 2544af34b57SMaxime Ripard const struct sunxi_sram_data *sram_data; 2554af34b57SMaxime Ripard struct sunxi_sram_desc *sram_desc; 2564af34b57SMaxime Ripard 2574af34b57SMaxime Ripard if (!dev || !dev->of_node) 2584af34b57SMaxime Ripard return -EINVAL; 2594af34b57SMaxime Ripard 2604af34b57SMaxime Ripard sram_data = sunxi_sram_of_parse(dev->of_node, NULL); 2614af34b57SMaxime Ripard if (IS_ERR(sram_data)) 2624af34b57SMaxime Ripard return -EINVAL; 2634af34b57SMaxime Ripard 2644af34b57SMaxime Ripard sram_desc = to_sram_desc(sram_data); 2654af34b57SMaxime Ripard 2664af34b57SMaxime Ripard spin_lock(&sram_lock); 2674af34b57SMaxime Ripard sram_desc->claimed = false; 2684af34b57SMaxime Ripard spin_unlock(&sram_lock); 2694af34b57SMaxime Ripard 2704af34b57SMaxime Ripard return 0; 2714af34b57SMaxime Ripard } 2724af34b57SMaxime Ripard EXPORT_SYMBOL(sunxi_sram_release); 2734af34b57SMaxime Ripard 2744af34b57SMaxime Ripard static int sunxi_sram_probe(struct platform_device *pdev) 2754af34b57SMaxime Ripard { 2764af34b57SMaxime Ripard struct resource *res; 2774af34b57SMaxime Ripard struct dentry *d; 2784af34b57SMaxime Ripard 2794af34b57SMaxime Ripard sram_dev = &pdev->dev; 2804af34b57SMaxime Ripard 2814af34b57SMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2824af34b57SMaxime Ripard base = devm_ioremap_resource(&pdev->dev, res); 2834af34b57SMaxime Ripard if (IS_ERR(base)) 2844af34b57SMaxime Ripard return PTR_ERR(base); 2854af34b57SMaxime Ripard 2864af34b57SMaxime Ripard of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 2874af34b57SMaxime Ripard 2884af34b57SMaxime Ripard d = debugfs_create_file("sram", S_IRUGO, NULL, NULL, 2894af34b57SMaxime Ripard &sunxi_sram_fops); 2904af34b57SMaxime Ripard if (!d) 2914af34b57SMaxime Ripard return -ENOMEM; 2924af34b57SMaxime Ripard 2934af34b57SMaxime Ripard return 0; 2944af34b57SMaxime Ripard } 2954af34b57SMaxime Ripard 2964af34b57SMaxime Ripard static const struct of_device_id sunxi_sram_dt_match[] = { 2974af34b57SMaxime Ripard { .compatible = "allwinner,sun4i-a10-sram-controller" }, 2984af34b57SMaxime Ripard { }, 2994af34b57SMaxime Ripard }; 3004af34b57SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); 3014af34b57SMaxime Ripard 3024af34b57SMaxime Ripard static struct platform_driver sunxi_sram_driver = { 3034af34b57SMaxime Ripard .driver = { 3044af34b57SMaxime Ripard .name = "sunxi-sram", 3054af34b57SMaxime Ripard .of_match_table = sunxi_sram_dt_match, 3064af34b57SMaxime Ripard }, 3074af34b57SMaxime Ripard .probe = sunxi_sram_probe, 3084af34b57SMaxime Ripard }; 3094af34b57SMaxime Ripard module_platform_driver(sunxi_sram_driver); 3104af34b57SMaxime Ripard 3114af34b57SMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 3124af34b57SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver"); 3134af34b57SMaxime Ripard MODULE_LICENSE("GPL"); 314