xref: /openbmc/linux/drivers/gpio/gpio-creg-snps.c (revision e91d0f05)
12505c7b0SEugeniy Paltsev // SPDX-License-Identifier: GPL-2.0+
22505c7b0SEugeniy Paltsev //
32505c7b0SEugeniy Paltsev // Synopsys CREG (Control REGisters) GPIO driver
42505c7b0SEugeniy Paltsev //
52505c7b0SEugeniy Paltsev // Copyright (C) 2018 Synopsys
62505c7b0SEugeniy Paltsev // Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
72505c7b0SEugeniy Paltsev 
82505c7b0SEugeniy Paltsev #include <linux/gpio/driver.h>
92505c7b0SEugeniy Paltsev #include <linux/io.h>
102505c7b0SEugeniy Paltsev #include <linux/of.h>
11*e91d0f05SRob Herring #include <linux/platform_device.h>
122505c7b0SEugeniy Paltsev 
132505c7b0SEugeniy Paltsev #define MAX_GPIO	32
142505c7b0SEugeniy Paltsev 
152505c7b0SEugeniy Paltsev struct creg_layout {
162505c7b0SEugeniy Paltsev 	u8 ngpio;
172505c7b0SEugeniy Paltsev 	u8 shift[MAX_GPIO];
182505c7b0SEugeniy Paltsev 	u8 on[MAX_GPIO];
192505c7b0SEugeniy Paltsev 	u8 off[MAX_GPIO];
202505c7b0SEugeniy Paltsev 	u8 bit_per_gpio[MAX_GPIO];
212505c7b0SEugeniy Paltsev };
222505c7b0SEugeniy Paltsev 
232505c7b0SEugeniy Paltsev struct creg_gpio {
242505c7b0SEugeniy Paltsev 	struct gpio_chip gc;
252505c7b0SEugeniy Paltsev 	void __iomem *regs;
262505c7b0SEugeniy Paltsev 	spinlock_t lock;
272505c7b0SEugeniy Paltsev 	const struct creg_layout *layout;
282505c7b0SEugeniy Paltsev };
292505c7b0SEugeniy Paltsev 
creg_gpio_set(struct gpio_chip * gc,unsigned int offset,int val)302505c7b0SEugeniy Paltsev static void creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
312505c7b0SEugeniy Paltsev {
322505c7b0SEugeniy Paltsev 	struct creg_gpio *hcg = gpiochip_get_data(gc);
332505c7b0SEugeniy Paltsev 	const struct creg_layout *layout = hcg->layout;
342505c7b0SEugeniy Paltsev 	u32 reg, reg_shift, value;
352505c7b0SEugeniy Paltsev 	unsigned long flags;
362505c7b0SEugeniy Paltsev 	int i;
372505c7b0SEugeniy Paltsev 
382505c7b0SEugeniy Paltsev 	value = val ? hcg->layout->on[offset] : hcg->layout->off[offset];
392505c7b0SEugeniy Paltsev 
402505c7b0SEugeniy Paltsev 	reg_shift = layout->shift[offset];
412505c7b0SEugeniy Paltsev 	for (i = 0; i < offset; i++)
422505c7b0SEugeniy Paltsev 		reg_shift += layout->bit_per_gpio[i] + layout->shift[i];
432505c7b0SEugeniy Paltsev 
442505c7b0SEugeniy Paltsev 	spin_lock_irqsave(&hcg->lock, flags);
452505c7b0SEugeniy Paltsev 	reg = readl(hcg->regs);
462505c7b0SEugeniy Paltsev 	reg &= ~(GENMASK(layout->bit_per_gpio[i] - 1, 0) << reg_shift);
472505c7b0SEugeniy Paltsev 	reg |=  (value << reg_shift);
482505c7b0SEugeniy Paltsev 	writel(reg, hcg->regs);
492505c7b0SEugeniy Paltsev 	spin_unlock_irqrestore(&hcg->lock, flags);
502505c7b0SEugeniy Paltsev }
512505c7b0SEugeniy Paltsev 
creg_gpio_dir_out(struct gpio_chip * gc,unsigned int offset,int val)522505c7b0SEugeniy Paltsev static int creg_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
532505c7b0SEugeniy Paltsev {
542505c7b0SEugeniy Paltsev 	creg_gpio_set(gc, offset, val);
552505c7b0SEugeniy Paltsev 
562505c7b0SEugeniy Paltsev 	return 0;
572505c7b0SEugeniy Paltsev }
582505c7b0SEugeniy Paltsev 
creg_gpio_validate_pg(struct device * dev,struct creg_gpio * hcg,int i)592505c7b0SEugeniy Paltsev static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg,
602505c7b0SEugeniy Paltsev 				 int i)
612505c7b0SEugeniy Paltsev {
622505c7b0SEugeniy Paltsev 	const struct creg_layout *layout = hcg->layout;
632505c7b0SEugeniy Paltsev 
642505c7b0SEugeniy Paltsev 	if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8)
652505c7b0SEugeniy Paltsev 		return -EINVAL;
662505c7b0SEugeniy Paltsev 
670d311d8bSSachin agarwal 	/* Check that on value fits its placeholder */
682505c7b0SEugeniy Paltsev 	if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i])
692505c7b0SEugeniy Paltsev 		return -EINVAL;
702505c7b0SEugeniy Paltsev 
710d311d8bSSachin agarwal 	/* Check that off value fits its placeholder */
722505c7b0SEugeniy Paltsev 	if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i])
732505c7b0SEugeniy Paltsev 		return -EINVAL;
742505c7b0SEugeniy Paltsev 
752505c7b0SEugeniy Paltsev 	if (layout->on[i] == layout->off[i])
762505c7b0SEugeniy Paltsev 		return -EINVAL;
772505c7b0SEugeniy Paltsev 
782505c7b0SEugeniy Paltsev 	return 0;
792505c7b0SEugeniy Paltsev }
802505c7b0SEugeniy Paltsev 
creg_gpio_validate(struct device * dev,struct creg_gpio * hcg,u32 ngpios)812505c7b0SEugeniy Paltsev static int creg_gpio_validate(struct device *dev, struct creg_gpio *hcg,
822505c7b0SEugeniy Paltsev 			      u32 ngpios)
832505c7b0SEugeniy Paltsev {
842505c7b0SEugeniy Paltsev 	u32 reg_len = 0;
852505c7b0SEugeniy Paltsev 	int i;
862505c7b0SEugeniy Paltsev 
872505c7b0SEugeniy Paltsev 	if (hcg->layout->ngpio < 1 || hcg->layout->ngpio > MAX_GPIO)
882505c7b0SEugeniy Paltsev 		return -EINVAL;
892505c7b0SEugeniy Paltsev 
902505c7b0SEugeniy Paltsev 	if (ngpios < 1 || ngpios > hcg->layout->ngpio) {
912505c7b0SEugeniy Paltsev 		dev_err(dev, "ngpios must be in [1:%u]\n", hcg->layout->ngpio);
922505c7b0SEugeniy Paltsev 		return -EINVAL;
932505c7b0SEugeniy Paltsev 	}
942505c7b0SEugeniy Paltsev 
952505c7b0SEugeniy Paltsev 	for (i = 0; i < hcg->layout->ngpio; i++) {
962505c7b0SEugeniy Paltsev 		if (creg_gpio_validate_pg(dev, hcg, i))
972505c7b0SEugeniy Paltsev 			return -EINVAL;
982505c7b0SEugeniy Paltsev 
992505c7b0SEugeniy Paltsev 		reg_len += hcg->layout->shift[i] + hcg->layout->bit_per_gpio[i];
1002505c7b0SEugeniy Paltsev 	}
1012505c7b0SEugeniy Paltsev 
1022505c7b0SEugeniy Paltsev 	/* Check that we fit in 32 bit register */
1032505c7b0SEugeniy Paltsev 	if (reg_len > 32)
1042505c7b0SEugeniy Paltsev 		return -EINVAL;
1052505c7b0SEugeniy Paltsev 
1062505c7b0SEugeniy Paltsev 	return 0;
1072505c7b0SEugeniy Paltsev }
1082505c7b0SEugeniy Paltsev 
1092505c7b0SEugeniy Paltsev static const struct creg_layout hsdk_cs_ctl = {
1102505c7b0SEugeniy Paltsev 	.ngpio		= 10,
1112505c7b0SEugeniy Paltsev 	.shift		= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
1122505c7b0SEugeniy Paltsev 	.off		= { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
1132505c7b0SEugeniy Paltsev 	.on		= { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
1142505c7b0SEugeniy Paltsev 	.bit_per_gpio	= { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }
1152505c7b0SEugeniy Paltsev };
1162505c7b0SEugeniy Paltsev 
1172505c7b0SEugeniy Paltsev static const struct creg_layout axs10x_flsh_cs_ctl = {
1182505c7b0SEugeniy Paltsev 	.ngpio		= 1,
1192505c7b0SEugeniy Paltsev 	.shift		= { 0 },
1202505c7b0SEugeniy Paltsev 	.off		= { 1 },
1212505c7b0SEugeniy Paltsev 	.on		= { 3 },
1222505c7b0SEugeniy Paltsev 	.bit_per_gpio	= { 2 }
1232505c7b0SEugeniy Paltsev };
1242505c7b0SEugeniy Paltsev 
1252505c7b0SEugeniy Paltsev static const struct of_device_id creg_gpio_ids[] = {
1262505c7b0SEugeniy Paltsev 	{
1272505c7b0SEugeniy Paltsev 		.compatible = "snps,creg-gpio-axs10x",
1282505c7b0SEugeniy Paltsev 		.data = &axs10x_flsh_cs_ctl
1292505c7b0SEugeniy Paltsev 	}, {
1302505c7b0SEugeniy Paltsev 		.compatible = "snps,creg-gpio-hsdk",
1312505c7b0SEugeniy Paltsev 		.data = &hsdk_cs_ctl
1322505c7b0SEugeniy Paltsev 	}, { /* sentinel */ }
1332505c7b0SEugeniy Paltsev };
1342505c7b0SEugeniy Paltsev 
creg_gpio_probe(struct platform_device * pdev)1352505c7b0SEugeniy Paltsev static int creg_gpio_probe(struct platform_device *pdev)
1362505c7b0SEugeniy Paltsev {
1372505c7b0SEugeniy Paltsev 	const struct of_device_id *match;
1382505c7b0SEugeniy Paltsev 	struct device *dev = &pdev->dev;
1392505c7b0SEugeniy Paltsev 	struct creg_gpio *hcg;
1402505c7b0SEugeniy Paltsev 	u32 ngpios;
1412505c7b0SEugeniy Paltsev 	int ret;
1422505c7b0SEugeniy Paltsev 
1432505c7b0SEugeniy Paltsev 	hcg = devm_kzalloc(dev, sizeof(struct creg_gpio), GFP_KERNEL);
1442505c7b0SEugeniy Paltsev 	if (!hcg)
1452505c7b0SEugeniy Paltsev 		return -ENOMEM;
1462505c7b0SEugeniy Paltsev 
147aba30f6fSYueHaibing 	hcg->regs = devm_platform_ioremap_resource(pdev, 0);
1482505c7b0SEugeniy Paltsev 	if (IS_ERR(hcg->regs))
1492505c7b0SEugeniy Paltsev 		return PTR_ERR(hcg->regs);
1502505c7b0SEugeniy Paltsev 
1512505c7b0SEugeniy Paltsev 	match = of_match_node(creg_gpio_ids, pdev->dev.of_node);
1522505c7b0SEugeniy Paltsev 	hcg->layout = match->data;
1532505c7b0SEugeniy Paltsev 	if (!hcg->layout)
1542505c7b0SEugeniy Paltsev 		return -EINVAL;
1552505c7b0SEugeniy Paltsev 
1562505c7b0SEugeniy Paltsev 	ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios);
1572505c7b0SEugeniy Paltsev 	if (ret)
1582505c7b0SEugeniy Paltsev 		return ret;
1592505c7b0SEugeniy Paltsev 
1602505c7b0SEugeniy Paltsev 	ret = creg_gpio_validate(dev, hcg, ngpios);
1612505c7b0SEugeniy Paltsev 	if (ret)
1622505c7b0SEugeniy Paltsev 		return ret;
1632505c7b0SEugeniy Paltsev 
1642505c7b0SEugeniy Paltsev 	spin_lock_init(&hcg->lock);
1652505c7b0SEugeniy Paltsev 
16645a541a6SAndy Shevchenko 	hcg->gc.parent = dev;
1672505c7b0SEugeniy Paltsev 	hcg->gc.label = dev_name(dev);
1682505c7b0SEugeniy Paltsev 	hcg->gc.base = -1;
1692505c7b0SEugeniy Paltsev 	hcg->gc.ngpio = ngpios;
1702505c7b0SEugeniy Paltsev 	hcg->gc.set = creg_gpio_set;
1712505c7b0SEugeniy Paltsev 	hcg->gc.direction_output = creg_gpio_dir_out;
1722505c7b0SEugeniy Paltsev 
1732505c7b0SEugeniy Paltsev 	ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg);
1742505c7b0SEugeniy Paltsev 	if (ret)
1752505c7b0SEugeniy Paltsev 		return ret;
1762505c7b0SEugeniy Paltsev 
1772505c7b0SEugeniy Paltsev 	dev_info(dev, "GPIO controller with %d gpios probed\n", ngpios);
1782505c7b0SEugeniy Paltsev 
1792505c7b0SEugeniy Paltsev 	return 0;
1802505c7b0SEugeniy Paltsev }
1812505c7b0SEugeniy Paltsev 
1822505c7b0SEugeniy Paltsev static struct platform_driver creg_gpio_snps_driver = {
1832505c7b0SEugeniy Paltsev 	.driver = {
1842505c7b0SEugeniy Paltsev 		.name = "snps-creg-gpio",
1852505c7b0SEugeniy Paltsev 		.of_match_table = creg_gpio_ids,
1862505c7b0SEugeniy Paltsev 	},
1872505c7b0SEugeniy Paltsev 	.probe  = creg_gpio_probe,
1882505c7b0SEugeniy Paltsev };
1892505c7b0SEugeniy Paltsev builtin_platform_driver(creg_gpio_snps_driver);
190