xref: /openbmc/linux/drivers/soc/sunxi/sunxi_sram.c (revision 8fed2ce9)
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