aboutsummaryrefslogtreecommitdiffstats
path: root/net/l2tp/l2tp_debugfs.c
AgeCommit message (Expand)Author
2018-08-03l2tp: ignore L2TP_ATTR_MTUGuillaume Nault
2018-07-27l2tp: drop ->mru from struct l2tp_sessionGuillaume Nault
2018-07-27l2tp: ignore L2TP_ATTR_DATA_SEQ netlink attributeGuillaume Nault
2018-06-26l2tp: remove .show from struct l2tp_tunnelGuillaume Nault
2018-04-27l2tp: consistent reference counting in procfs and debufsGuillaume Nault
2018-04-22l2tp: fix {pppol2tp, l2tp_dfs}_seq_stop() in case of seq_file overflowGuillaume Nault
2018-04-13l2tp: hold reference on tunnels printed in l2tp/tunnels debugfs fileGuillaume Nault
2018-01-19l2tp: remove l2specific_len configurable parameterLorenzo Bianconi
2018-01-05l2tp: remove configurable payload offsetJames Chapman
2018-01-05l2tp: revert "l2tp: add peer_offset parameter"James Chapman
2017-12-27l2tp: add peer_offset parameterLorenzo Bianconi
2017-11-01l2tp: remove ->ref() and ->deref()Guillaume Nault
2017-07-04net, l2tp: convert l2tp_tunnel.ref_count from atomic_t to refcount_tReshetova, Elena
2017-07-01net: convert sock.sk_refcnt from atomic_t to refcount_tReshetova, Elena
2017-04-04l2tp: take reference on sessions being dumpedGuillaume Nault
2013-10-09ipv6: make lookups simpler and fasterEric Dumazet
2013-03-20l2tp: avoid deadlock in l2tp stats updateTom Parkin
2012-05-17net: l2tp: Standardize logging stylesJoe Perches
2012-05-01l2tp: show IPv6 addresses in l2tp debugfs fileChris Elston
2011-06-05fix return values of l2tp_dfs_seq_open()Al Viro
2010-11-01l2tp: kzalloc with swapped params in l2tp_dfs_seq_openDr. David Alan Gilbert
2010-04-15net/l2tp/l2tp_debugfs.c: Convert NIPQUAD to %pI4Joe Perches
2010-04-03l2tp: Add debugfs files for dumping l2tp debug infoJames Chapman
ighlight .nl { color: #f8f8f2 } /* Name.Label */ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ .highlight .nx { color: #a6e22e } /* Name.Other */ .highlight .py { color: #f8f8f2 } /* Name.Property */ .highlight .nt { color: #f92672 } /* Name.Tag */ .highlight .nv { color: #f8f8f2 } /* Name.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018 Spreadtrum Communications Inc.
 * Copyright (C) 2018 Linaro Ltd.
 */

#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>

/* GPIO registers definition */
#define SPRD_GPIO_DATA		0x0
#define SPRD_GPIO_DMSK		0x4
#define SPRD_GPIO_DIR		0x8
#define SPRD_GPIO_IS		0xc
#define SPRD_GPIO_IBE		0x10
#define SPRD_GPIO_IEV		0x14
#define SPRD_GPIO_IE		0x18
#define SPRD_GPIO_RIS		0x1c
#define SPRD_GPIO_MIS		0x20
#define SPRD_GPIO_IC		0x24
#define SPRD_GPIO_INEN		0x28

/* We have 16 banks GPIOs and each bank contain 16 GPIOs */
#define SPRD_GPIO_BANK_NR	16
#define SPRD_GPIO_NR		256
#define SPRD_GPIO_BANK_SIZE	0x80
#define SPRD_GPIO_BANK_MASK	GENMASK(15, 0)
#define SPRD_GPIO_BIT(x)	((x) & (SPRD_GPIO_BANK_NR - 1))

struct sprd_gpio {
	struct gpio_chip chip;
	void __iomem *base;
	spinlock_t lock;
	int irq;
};

static inline void __iomem *sprd_gpio_bank_base(struct sprd_gpio *sprd_gpio,
						unsigned int bank)
{
	return sprd_gpio->base + SPRD_GPIO_BANK_SIZE * bank;
}

static void sprd_gpio_update(struct gpio_chip *chip, unsigned int offset,
			     u16 reg, int val)
{
	struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
	void __iomem *base = sprd_gpio_bank_base(sprd_gpio,
						 offset / SPRD_GPIO_BANK_NR);
	unsigned long flags;
	u32 tmp;

	spin_lock_irqsave(&sprd_gpio->lock, flags);
	tmp = readl_relaxed(base + reg);

	if (val)
		tmp |= BIT(SPRD_GPIO_BIT(offset));
	else
		tmp &= ~BIT(SPRD_GPIO_BIT(offset));

	writel_relaxed(tmp, base + reg);
	spin_unlock_irqrestore(&sprd_gpio->lock, flags);
}

static int sprd_gpio_read(struct gpio_chip *chip, unsigned int offset, u16 reg)
{
	struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
	void __iomem *base = sprd_gpio_bank_base(sprd_gpio,
						 offset / SPRD_GPIO_BANK_NR);

	return !!(readl_relaxed(base + reg) & BIT(SPRD_GPIO_BIT(offset)));
}

static int sprd_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
	sprd_gpio_update(chip, offset, SPRD_GPIO_DMSK, 1);
	return 0;
}

static void sprd_gpio_free(struct gpio_chip *chip, unsigned int offset)
{
	sprd_gpio_update(chip, offset, SPRD_GPIO_DMSK, 0);
}

static int sprd_gpio_direction_input(struct gpio_chip *chip,
				     unsigned int offset)
{
	sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 0);
	sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 1);
	return 0;
}

static int sprd_gpio_direction_output(struct gpio_chip *chip,
				      unsigned int offset, int value)
{
	sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 1);
	sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 0);
	sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
	return 0;
}

static int sprd_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
	return sprd_gpio_read(chip, offset, SPRD_GPIO_DATA);
}

static void sprd_gpio_set(struct gpio_chip *chip, unsigned int offset,
			  int value)
{
	sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
}

static void sprd_gpio_irq_mask(struct irq_data *data)
{
	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
	u32 offset = irqd_to_hwirq(data);

	sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 0);
}

static void sprd_gpio_irq_ack(struct irq_data *data)
{
	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
	u32 offset = irqd_to_hwirq(data);

	sprd_gpio_update(chip, offset, SPRD_GPIO_IC, 1);
}

static void sprd_gpio_irq_unmask(struct irq_data *data)
{
	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
	u32 offset = irqd_to_hwirq(data);

	sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 1);
}

static int sprd_gpio_irq_set_type(struct irq_data *data,
				  unsigned int flow_type)
{
	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
	u32 offset = irqd_to_hwirq(data);

	switch (flow_type) {
	case IRQ_TYPE_EDGE_RISING:
		sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 1);
		irq_set_handler_locked(data, handle_edge_irq);
		break;
	case IRQ_TYPE_EDGE_FALLING:
		sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 0);
		irq_set_handler_locked(data, handle_edge_irq);
		break;
	case IRQ_TYPE_EDGE_BOTH:
		sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 1);
		irq_set_handler_locked(data, handle_edge_irq);
		break;
	case IRQ_TYPE_LEVEL_HIGH:
		sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 1);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 1);
		irq_set_handler_locked(data, handle_level_irq);
		break;
	case IRQ_TYPE_LEVEL_LOW:
		sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 1);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
		sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 0);
		irq_set_handler_locked(data, handle_level_irq);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static void sprd_gpio_irq_handler(struct irq_desc *desc)
{
	struct gpio_chip *chip = irq_desc_get_handler_data(desc);
	struct irq_chip *ic = irq_desc_get_chip(desc);
	struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
	u32 bank, n, girq;

	chained_irq_enter(ic, desc);

	for (bank = 0; bank * SPRD_GPIO_BANK_NR < chip->ngpio; bank++) {
		void __iomem *base = sprd_gpio_bank_base(sprd_gpio, bank);
		unsigned long reg = readl_relaxed(base + SPRD_GPIO_MIS) &
			SPRD_GPIO_BANK_MASK;

		for_each_set_bit(n, &reg, SPRD_GPIO_BANK_NR) {
			girq = irq_find_mapping(chip->irq.domain,
						bank * SPRD_GPIO_BANK_NR + n);

			generic_handle_irq(girq);
		}

	}
	chained_irq_exit(ic, desc);
}

static struct irq_chip sprd_gpio_irqchip = {
	.name = "sprd-gpio",
	.irq_ack = sprd_gpio_irq_ack,
	.irq_mask = sprd_gpio_irq_mask,
	.irq_unmask = sprd_gpio_irq_unmask,
	.irq_set_type = sprd_gpio_irq_set_type,
	.flags = IRQCHIP_SKIP_SET_WAKE,
};

static int sprd_gpio_probe(struct platform_device *pdev)
{
	struct gpio_irq_chip *irq;
	struct sprd_gpio *sprd_gpio;
	int ret;

	sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
	if (!sprd_gpio)
		return -ENOMEM;

	sprd_gpio->irq = platform_get_irq(pdev, 0);
	if (sprd_gpio->irq < 0) {
		dev_err(&pdev->dev, "Failed to get GPIO interrupt.\n");
		return sprd_gpio->irq;
	}

	sprd_gpio->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(sprd_gpio->base))
		return PTR_ERR(sprd_gpio->base);

	spin_lock_init(&sprd_gpio->lock);

	sprd_gpio->chip.label = dev_name(&pdev->dev);
	sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
	sprd_gpio->chip.base = -1;
	sprd_gpio->chip.parent = &pdev->dev;
	sprd_gpio->chip.of_node = pdev->dev.of_node;
	sprd_gpio->chip.request = sprd_gpio_request;
	sprd_gpio->chip.free = sprd_gpio_free;
	sprd_gpio->chip.get = sprd_gpio_get;
	sprd_gpio->chip.set = sprd_gpio_set;
	sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
	sprd_gpio->chip.direction_output = sprd_gpio_direction_output;

	irq = &sprd_gpio->chip.irq;
	irq->chip = &sprd_gpio_irqchip;
	irq->handler = handle_bad_irq;
	irq->default_type = IRQ_TYPE_NONE;
	irq->parent_handler = sprd_gpio_irq_handler;
	irq->parent_handler_data = sprd_gpio;
	irq->num_parents = 1;
	irq->parents = &sprd_gpio->irq;

	ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
	if (ret < 0) {
		dev_err(&pdev->dev, "Could not register gpiochip %d\n", ret);
		return ret;
	}

	platform_set_drvdata(pdev, sprd_gpio);
	return 0;
}

static const struct of_device_id sprd_gpio_of_match[] = {
	{ .compatible = "sprd,sc9860-gpio", },
	{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, sprd_gpio_of_match);

static struct platform_driver sprd_gpio_driver = {
	.probe = sprd_gpio_probe,
	.driver = {
		.name = "sprd-gpio",
		.of_match_table	= sprd_gpio_of_match,
	},
};

module_platform_driver_probe(sprd_gpio_driver, sprd_gpio_probe);

MODULE_DESCRIPTION("Spreadtrum GPIO driver");
MODULE_LICENSE("GPL v2");