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 725e4fb642SIcenowy Zheng static struct sunxi_sram_desc sun50i_a64_sram_c = { 735e4fb642SIcenowy Zheng .data = SUNXI_SRAM_DATA("C", 0x4, 24, 1, 745e4fb642SIcenowy Zheng SUNXI_SRAM_MAP(0, 1, "cpu"), 755e4fb642SIcenowy Zheng SUNXI_SRAM_MAP(1, 0, "de2")), 765e4fb642SIcenowy Zheng }; 775e4fb642SIcenowy Zheng 784af34b57SMaxime Ripard static const struct of_device_id sunxi_sram_dt_ids[] = { 794af34b57SMaxime Ripard { 804af34b57SMaxime Ripard .compatible = "allwinner,sun4i-a10-sram-a3-a4", 814af34b57SMaxime Ripard .data = &sun4i_a10_sram_a3_a4.data, 824af34b57SMaxime Ripard }, 834af34b57SMaxime Ripard { 844af34b57SMaxime Ripard .compatible = "allwinner,sun4i-a10-sram-d", 854af34b57SMaxime Ripard .data = &sun4i_a10_sram_d.data, 864af34b57SMaxime Ripard }, 875e4fb642SIcenowy Zheng { 885e4fb642SIcenowy Zheng .compatible = "allwinner,sun50i-a64-sram-c", 895e4fb642SIcenowy Zheng .data = &sun50i_a64_sram_c.data, 905e4fb642SIcenowy Zheng }, 914af34b57SMaxime Ripard {} 924af34b57SMaxime Ripard }; 934af34b57SMaxime Ripard 944af34b57SMaxime Ripard static struct device *sram_dev; 954af34b57SMaxime Ripard static LIST_HEAD(claimed_sram); 964af34b57SMaxime Ripard static DEFINE_SPINLOCK(sram_lock); 974af34b57SMaxime Ripard static void __iomem *base; 984af34b57SMaxime Ripard 994af34b57SMaxime Ripard static int sunxi_sram_show(struct seq_file *s, void *data) 1004af34b57SMaxime Ripard { 1014af34b57SMaxime Ripard struct device_node *sram_node, *section_node; 1024af34b57SMaxime Ripard const struct sunxi_sram_data *sram_data; 1034af34b57SMaxime Ripard const struct of_device_id *match; 1044af34b57SMaxime Ripard struct sunxi_sram_func *func; 1054af34b57SMaxime Ripard const __be32 *sram_addr_p, *section_addr_p; 1064af34b57SMaxime Ripard u32 val; 1074af34b57SMaxime Ripard 1084af34b57SMaxime Ripard seq_puts(s, "Allwinner sunXi SRAM\n"); 1094af34b57SMaxime Ripard seq_puts(s, "--------------------\n\n"); 1104af34b57SMaxime Ripard 1114af34b57SMaxime Ripard for_each_child_of_node(sram_dev->of_node, sram_node) { 1124af34b57SMaxime Ripard sram_addr_p = of_get_address(sram_node, 0, NULL, NULL); 1134af34b57SMaxime Ripard 1144af34b57SMaxime Ripard seq_printf(s, "sram@%08x\n", 1154af34b57SMaxime Ripard be32_to_cpu(*sram_addr_p)); 1164af34b57SMaxime Ripard 1174af34b57SMaxime Ripard for_each_child_of_node(sram_node, section_node) { 1184af34b57SMaxime Ripard match = of_match_node(sunxi_sram_dt_ids, section_node); 1194af34b57SMaxime Ripard if (!match) 1204af34b57SMaxime Ripard continue; 1214af34b57SMaxime Ripard sram_data = match->data; 1224af34b57SMaxime Ripard 1234af34b57SMaxime Ripard section_addr_p = of_get_address(section_node, 0, 1244af34b57SMaxime Ripard NULL, NULL); 1254af34b57SMaxime Ripard 1264af34b57SMaxime Ripard seq_printf(s, "\tsection@%04x\t(%s)\n", 1274af34b57SMaxime Ripard be32_to_cpu(*section_addr_p), 1284af34b57SMaxime Ripard sram_data->name); 1294af34b57SMaxime Ripard 1304af34b57SMaxime Ripard val = readl(base + sram_data->reg); 1314af34b57SMaxime Ripard val >>= sram_data->offset; 132febe6569SJens Kuske val &= GENMASK(sram_data->width - 1, 0); 1334af34b57SMaxime Ripard 1344af34b57SMaxime Ripard for (func = sram_data->func; func->func; func++) { 1354af34b57SMaxime Ripard seq_printf(s, "\t\t%s%c\n", func->func, 1368fed2ce9SIcenowy Zheng func->reg_val == val ? 1378fed2ce9SIcenowy Zheng '*' : ' '); 1384af34b57SMaxime Ripard } 1394af34b57SMaxime Ripard } 1404af34b57SMaxime Ripard 1414af34b57SMaxime Ripard seq_puts(s, "\n"); 1424af34b57SMaxime Ripard } 1434af34b57SMaxime Ripard 1444af34b57SMaxime Ripard return 0; 1454af34b57SMaxime Ripard } 1464af34b57SMaxime Ripard 1474af34b57SMaxime Ripard static int sunxi_sram_open(struct inode *inode, struct file *file) 1484af34b57SMaxime Ripard { 1494af34b57SMaxime Ripard return single_open(file, sunxi_sram_show, inode->i_private); 1504af34b57SMaxime Ripard } 1514af34b57SMaxime Ripard 1524af34b57SMaxime Ripard static const struct file_operations sunxi_sram_fops = { 1534af34b57SMaxime Ripard .open = sunxi_sram_open, 1544af34b57SMaxime Ripard .read = seq_read, 1554af34b57SMaxime Ripard .llseek = seq_lseek, 1564af34b57SMaxime Ripard .release = single_release, 1574af34b57SMaxime Ripard }; 1584af34b57SMaxime Ripard 1594af34b57SMaxime Ripard static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data) 1604af34b57SMaxime Ripard { 1614af34b57SMaxime Ripard return container_of(data, struct sunxi_sram_desc, data); 1624af34b57SMaxime Ripard } 1634af34b57SMaxime Ripard 1644af34b57SMaxime Ripard static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node, 1658fed2ce9SIcenowy Zheng unsigned int *reg_value) 1664af34b57SMaxime Ripard { 1674af34b57SMaxime Ripard const struct of_device_id *match; 1688fed2ce9SIcenowy Zheng const struct sunxi_sram_data *data; 1698fed2ce9SIcenowy Zheng struct sunxi_sram_func *func; 1704af34b57SMaxime Ripard struct of_phandle_args args; 1718fed2ce9SIcenowy Zheng u8 val; 1724af34b57SMaxime Ripard int ret; 1734af34b57SMaxime Ripard 1744af34b57SMaxime Ripard ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0, 1754af34b57SMaxime Ripard &args); 1764af34b57SMaxime Ripard if (ret) 1774af34b57SMaxime Ripard return ERR_PTR(ret); 1784af34b57SMaxime Ripard 1794af34b57SMaxime Ripard if (!of_device_is_available(args.np)) { 1804af34b57SMaxime Ripard ret = -EBUSY; 1814af34b57SMaxime Ripard goto err; 1824af34b57SMaxime Ripard } 1834af34b57SMaxime Ripard 1848fed2ce9SIcenowy Zheng val = args.args[0]; 1854af34b57SMaxime Ripard 1864af34b57SMaxime Ripard match = of_match_node(sunxi_sram_dt_ids, args.np); 1874af34b57SMaxime Ripard if (!match) { 1884af34b57SMaxime Ripard ret = -EINVAL; 1894af34b57SMaxime Ripard goto err; 1904af34b57SMaxime Ripard } 1914af34b57SMaxime Ripard 1928fed2ce9SIcenowy Zheng data = match->data; 1938fed2ce9SIcenowy Zheng if (!data) { 1948fed2ce9SIcenowy Zheng ret = -EINVAL; 1958fed2ce9SIcenowy Zheng goto err; 1968fed2ce9SIcenowy Zheng }; 1978fed2ce9SIcenowy Zheng 1988fed2ce9SIcenowy Zheng for (func = data->func; func->func; func++) { 1998fed2ce9SIcenowy Zheng if (val == func->val) { 2008fed2ce9SIcenowy Zheng if (reg_value) 2018fed2ce9SIcenowy Zheng *reg_value = func->reg_val; 2028fed2ce9SIcenowy Zheng 2038fed2ce9SIcenowy Zheng break; 2048fed2ce9SIcenowy Zheng } 2058fed2ce9SIcenowy Zheng } 2068fed2ce9SIcenowy Zheng 2078fed2ce9SIcenowy Zheng if (!func->func) { 2088fed2ce9SIcenowy Zheng ret = -EINVAL; 2098fed2ce9SIcenowy Zheng goto err; 2108fed2ce9SIcenowy Zheng } 2118fed2ce9SIcenowy Zheng 2124af34b57SMaxime Ripard of_node_put(args.np); 2134af34b57SMaxime Ripard return match->data; 2144af34b57SMaxime Ripard 2154af34b57SMaxime Ripard err: 2164af34b57SMaxime Ripard of_node_put(args.np); 2174af34b57SMaxime Ripard return ERR_PTR(ret); 2184af34b57SMaxime Ripard } 2194af34b57SMaxime Ripard 2204af34b57SMaxime Ripard int sunxi_sram_claim(struct device *dev) 2214af34b57SMaxime Ripard { 2224af34b57SMaxime Ripard const struct sunxi_sram_data *sram_data; 2234af34b57SMaxime Ripard struct sunxi_sram_desc *sram_desc; 2244af34b57SMaxime Ripard unsigned int device; 2254af34b57SMaxime Ripard u32 val, mask; 2264af34b57SMaxime Ripard 2274af34b57SMaxime Ripard if (IS_ERR(base)) 2282262a65fSIcenowy Zheng return PTR_ERR(base); 2292262a65fSIcenowy Zheng 2302262a65fSIcenowy Zheng if (!base) 2314af34b57SMaxime Ripard return -EPROBE_DEFER; 2324af34b57SMaxime Ripard 2334af34b57SMaxime Ripard if (!dev || !dev->of_node) 2344af34b57SMaxime Ripard return -EINVAL; 2354af34b57SMaxime Ripard 2364af34b57SMaxime Ripard sram_data = sunxi_sram_of_parse(dev->of_node, &device); 2374af34b57SMaxime Ripard if (IS_ERR(sram_data)) 2384af34b57SMaxime Ripard return PTR_ERR(sram_data); 2394af34b57SMaxime Ripard 2404af34b57SMaxime Ripard sram_desc = to_sram_desc(sram_data); 2414af34b57SMaxime Ripard 2424af34b57SMaxime Ripard spin_lock(&sram_lock); 2434af34b57SMaxime Ripard 2444af34b57SMaxime Ripard if (sram_desc->claimed) { 2454af34b57SMaxime Ripard spin_unlock(&sram_lock); 2464af34b57SMaxime Ripard return -EBUSY; 2474af34b57SMaxime Ripard } 2484af34b57SMaxime Ripard 249febe6569SJens Kuske mask = GENMASK(sram_data->offset + sram_data->width - 1, 250febe6569SJens Kuske sram_data->offset); 2514af34b57SMaxime Ripard val = readl(base + sram_data->reg); 2524af34b57SMaxime Ripard val &= ~mask; 2534af34b57SMaxime Ripard writel(val | ((device << sram_data->offset) & mask), 2544af34b57SMaxime Ripard base + sram_data->reg); 2554af34b57SMaxime Ripard 2564af34b57SMaxime Ripard spin_unlock(&sram_lock); 2574af34b57SMaxime Ripard 2584af34b57SMaxime Ripard return 0; 2594af34b57SMaxime Ripard } 2604af34b57SMaxime Ripard EXPORT_SYMBOL(sunxi_sram_claim); 2614af34b57SMaxime Ripard 2624af34b57SMaxime Ripard int sunxi_sram_release(struct device *dev) 2634af34b57SMaxime Ripard { 2644af34b57SMaxime Ripard const struct sunxi_sram_data *sram_data; 2654af34b57SMaxime Ripard struct sunxi_sram_desc *sram_desc; 2664af34b57SMaxime Ripard 2674af34b57SMaxime Ripard if (!dev || !dev->of_node) 2684af34b57SMaxime Ripard return -EINVAL; 2694af34b57SMaxime Ripard 2704af34b57SMaxime Ripard sram_data = sunxi_sram_of_parse(dev->of_node, NULL); 2714af34b57SMaxime Ripard if (IS_ERR(sram_data)) 2724af34b57SMaxime Ripard return -EINVAL; 2734af34b57SMaxime Ripard 2744af34b57SMaxime Ripard sram_desc = to_sram_desc(sram_data); 2754af34b57SMaxime Ripard 2764af34b57SMaxime Ripard spin_lock(&sram_lock); 2774af34b57SMaxime Ripard sram_desc->claimed = false; 2784af34b57SMaxime Ripard spin_unlock(&sram_lock); 2794af34b57SMaxime Ripard 2804af34b57SMaxime Ripard return 0; 2814af34b57SMaxime Ripard } 2824af34b57SMaxime Ripard EXPORT_SYMBOL(sunxi_sram_release); 2834af34b57SMaxime Ripard 2844af34b57SMaxime Ripard static int sunxi_sram_probe(struct platform_device *pdev) 2854af34b57SMaxime Ripard { 2864af34b57SMaxime Ripard struct resource *res; 2874af34b57SMaxime Ripard struct dentry *d; 2884af34b57SMaxime Ripard 2894af34b57SMaxime Ripard sram_dev = &pdev->dev; 2904af34b57SMaxime Ripard 2914af34b57SMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2924af34b57SMaxime Ripard base = devm_ioremap_resource(&pdev->dev, res); 2934af34b57SMaxime Ripard if (IS_ERR(base)) 2944af34b57SMaxime Ripard return PTR_ERR(base); 2954af34b57SMaxime Ripard 2964af34b57SMaxime Ripard of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 2974af34b57SMaxime Ripard 2984af34b57SMaxime Ripard d = debugfs_create_file("sram", S_IRUGO, NULL, NULL, 2994af34b57SMaxime Ripard &sunxi_sram_fops); 3004af34b57SMaxime Ripard if (!d) 3014af34b57SMaxime Ripard return -ENOMEM; 3024af34b57SMaxime Ripard 3034af34b57SMaxime Ripard return 0; 3044af34b57SMaxime Ripard } 3054af34b57SMaxime Ripard 3064af34b57SMaxime Ripard static const struct of_device_id sunxi_sram_dt_match[] = { 3074af34b57SMaxime Ripard { .compatible = "allwinner,sun4i-a10-sram-controller" }, 3085e4fb642SIcenowy Zheng { .compatible = "allwinner,sun50i-a64-sram-controller" }, 3094af34b57SMaxime Ripard { }, 3104af34b57SMaxime Ripard }; 3114af34b57SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); 3124af34b57SMaxime Ripard 3134af34b57SMaxime Ripard static struct platform_driver sunxi_sram_driver = { 3144af34b57SMaxime Ripard .driver = { 3154af34b57SMaxime Ripard .name = "sunxi-sram", 3164af34b57SMaxime Ripard .of_match_table = sunxi_sram_dt_match, 3174af34b57SMaxime Ripard }, 3184af34b57SMaxime Ripard .probe = sunxi_sram_probe, 3194af34b57SMaxime Ripard }; 3204af34b57SMaxime Ripard module_platform_driver(sunxi_sram_driver); 3214af34b57SMaxime Ripard 3224af34b57SMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 3234af34b57SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver"); 3244af34b57SMaxime Ripard MODULE_LICENSE("GPL"); 325