162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Generic device tree based pinctrl driver for one register per pin
362306a36Sopenharmony_ci * type pinmux controllers
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
862306a36Sopenharmony_ci * License version 2. This program is licensed "as is" without any
962306a36Sopenharmony_ci * warranty of any kind, whether express or implied.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/list.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/of_irq.h>
2362306a36Sopenharmony_ci#include <linux/seq_file.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
2662306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h>
2762306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
2862306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <linux/platform_data/pinctrl-single.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "core.h"
3362306a36Sopenharmony_ci#include "devicetree.h"
3462306a36Sopenharmony_ci#include "pinconf.h"
3562306a36Sopenharmony_ci#include "pinmux.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define DRIVER_NAME			"pinctrl-single"
3862306a36Sopenharmony_ci#define PCS_OFF_DISABLED		~0U
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/**
4162306a36Sopenharmony_ci * struct pcs_func_vals - mux function register offset and value pair
4262306a36Sopenharmony_ci * @reg:	register virtual address
4362306a36Sopenharmony_ci * @val:	register value
4462306a36Sopenharmony_ci * @mask:	mask
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_cistruct pcs_func_vals {
4762306a36Sopenharmony_ci	void __iomem *reg;
4862306a36Sopenharmony_ci	unsigned val;
4962306a36Sopenharmony_ci	unsigned mask;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/**
5362306a36Sopenharmony_ci * struct pcs_conf_vals - pinconf parameter, pinconf register offset
5462306a36Sopenharmony_ci * and value, enable, disable, mask
5562306a36Sopenharmony_ci * @param:	config parameter
5662306a36Sopenharmony_ci * @val:	user input bits in the pinconf register
5762306a36Sopenharmony_ci * @enable:	enable bits in the pinconf register
5862306a36Sopenharmony_ci * @disable:	disable bits in the pinconf register
5962306a36Sopenharmony_ci * @mask:	mask bits in the register value
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistruct pcs_conf_vals {
6262306a36Sopenharmony_ci	enum pin_config_param param;
6362306a36Sopenharmony_ci	unsigned val;
6462306a36Sopenharmony_ci	unsigned enable;
6562306a36Sopenharmony_ci	unsigned disable;
6662306a36Sopenharmony_ci	unsigned mask;
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/**
7062306a36Sopenharmony_ci * struct pcs_conf_type - pinconf property name, pinconf param pair
7162306a36Sopenharmony_ci * @name:	property name in DTS file
7262306a36Sopenharmony_ci * @param:	config parameter
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistruct pcs_conf_type {
7562306a36Sopenharmony_ci	const char *name;
7662306a36Sopenharmony_ci	enum pin_config_param param;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/**
8062306a36Sopenharmony_ci * struct pcs_function - pinctrl function
8162306a36Sopenharmony_ci * @name:	pinctrl function name
8262306a36Sopenharmony_ci * @vals:	register and vals array
8362306a36Sopenharmony_ci * @nvals:	number of entries in vals array
8462306a36Sopenharmony_ci * @pgnames:	array of pingroup names the function uses
8562306a36Sopenharmony_ci * @npgnames:	number of pingroup names the function uses
8662306a36Sopenharmony_ci * @conf:	array of pin configurations
8762306a36Sopenharmony_ci * @nconfs:	number of pin configurations available
8862306a36Sopenharmony_ci * @node:	list node
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistruct pcs_function {
9162306a36Sopenharmony_ci	const char *name;
9262306a36Sopenharmony_ci	struct pcs_func_vals *vals;
9362306a36Sopenharmony_ci	unsigned nvals;
9462306a36Sopenharmony_ci	const char **pgnames;
9562306a36Sopenharmony_ci	int npgnames;
9662306a36Sopenharmony_ci	struct pcs_conf_vals *conf;
9762306a36Sopenharmony_ci	int nconfs;
9862306a36Sopenharmony_ci	struct list_head node;
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/**
10262306a36Sopenharmony_ci * struct pcs_gpiofunc_range - pin ranges with same mux value of gpio function
10362306a36Sopenharmony_ci * @offset:	offset base of pins
10462306a36Sopenharmony_ci * @npins:	number pins with the same mux value of gpio function
10562306a36Sopenharmony_ci * @gpiofunc:	mux value of gpio function
10662306a36Sopenharmony_ci * @node:	list node
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistruct pcs_gpiofunc_range {
10962306a36Sopenharmony_ci	unsigned offset;
11062306a36Sopenharmony_ci	unsigned npins;
11162306a36Sopenharmony_ci	unsigned gpiofunc;
11262306a36Sopenharmony_ci	struct list_head node;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/**
11662306a36Sopenharmony_ci * struct pcs_data - wrapper for data needed by pinctrl framework
11762306a36Sopenharmony_ci * @pa:		pindesc array
11862306a36Sopenharmony_ci * @cur:	index to current element
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci * REVISIT: We should be able to drop this eventually by adding
12162306a36Sopenharmony_ci * support for registering pins individually in the pinctrl
12262306a36Sopenharmony_ci * framework for those drivers that don't need a static array.
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistruct pcs_data {
12562306a36Sopenharmony_ci	struct pinctrl_pin_desc *pa;
12662306a36Sopenharmony_ci	int cur;
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/**
13062306a36Sopenharmony_ci * struct pcs_soc_data - SoC specific settings
13162306a36Sopenharmony_ci * @flags:	initial SoC specific PCS_FEAT_xxx values
13262306a36Sopenharmony_ci * @irq:	optional interrupt for the controller
13362306a36Sopenharmony_ci * @irq_enable_mask:	optional SoC specific interrupt enable mask
13462306a36Sopenharmony_ci * @irq_status_mask:	optional SoC specific interrupt status mask
13562306a36Sopenharmony_ci * @rearm:	optional SoC specific wake-up rearm function
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistruct pcs_soc_data {
13862306a36Sopenharmony_ci	unsigned flags;
13962306a36Sopenharmony_ci	int irq;
14062306a36Sopenharmony_ci	unsigned irq_enable_mask;
14162306a36Sopenharmony_ci	unsigned irq_status_mask;
14262306a36Sopenharmony_ci	void (*rearm)(void);
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * struct pcs_device - pinctrl device instance
14762306a36Sopenharmony_ci * @res:	resources
14862306a36Sopenharmony_ci * @base:	virtual address of the controller
14962306a36Sopenharmony_ci * @saved_vals: saved values for the controller
15062306a36Sopenharmony_ci * @size:	size of the ioremapped area
15162306a36Sopenharmony_ci * @dev:	device entry
15262306a36Sopenharmony_ci * @np:		device tree node
15362306a36Sopenharmony_ci * @pctl:	pin controller device
15462306a36Sopenharmony_ci * @flags:	mask of PCS_FEAT_xxx values
15562306a36Sopenharmony_ci * @missing_nr_pinctrl_cells: for legacy binding, may go away
15662306a36Sopenharmony_ci * @socdata:	soc specific data
15762306a36Sopenharmony_ci * @lock:	spinlock for register access
15862306a36Sopenharmony_ci * @mutex:	mutex protecting the lists
15962306a36Sopenharmony_ci * @width:	bits per mux register
16062306a36Sopenharmony_ci * @fmask:	function register mask
16162306a36Sopenharmony_ci * @fshift:	function register shift
16262306a36Sopenharmony_ci * @foff:	value to turn mux off
16362306a36Sopenharmony_ci * @fmax:	max number of functions in fmask
16462306a36Sopenharmony_ci * @bits_per_mux: number of bits per mux
16562306a36Sopenharmony_ci * @bits_per_pin: number of bits per pin
16662306a36Sopenharmony_ci * @pins:	physical pins on the SoC
16762306a36Sopenharmony_ci * @gpiofuncs:	list of gpio functions
16862306a36Sopenharmony_ci * @irqs:	list of interrupt registers
16962306a36Sopenharmony_ci * @chip:	chip container for this instance
17062306a36Sopenharmony_ci * @domain:	IRQ domain for this instance
17162306a36Sopenharmony_ci * @desc:	pin controller descriptor
17262306a36Sopenharmony_ci * @read:	register read function to use
17362306a36Sopenharmony_ci * @write:	register write function to use
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_cistruct pcs_device {
17662306a36Sopenharmony_ci	struct resource *res;
17762306a36Sopenharmony_ci	void __iomem *base;
17862306a36Sopenharmony_ci	void *saved_vals;
17962306a36Sopenharmony_ci	unsigned size;
18062306a36Sopenharmony_ci	struct device *dev;
18162306a36Sopenharmony_ci	struct device_node *np;
18262306a36Sopenharmony_ci	struct pinctrl_dev *pctl;
18362306a36Sopenharmony_ci	unsigned flags;
18462306a36Sopenharmony_ci#define PCS_CONTEXT_LOSS_OFF	(1 << 3)
18562306a36Sopenharmony_ci#define PCS_QUIRK_SHARED_IRQ	(1 << 2)
18662306a36Sopenharmony_ci#define PCS_FEAT_IRQ		(1 << 1)
18762306a36Sopenharmony_ci#define PCS_FEAT_PINCONF	(1 << 0)
18862306a36Sopenharmony_ci	struct property *missing_nr_pinctrl_cells;
18962306a36Sopenharmony_ci	struct pcs_soc_data socdata;
19062306a36Sopenharmony_ci	raw_spinlock_t lock;
19162306a36Sopenharmony_ci	struct mutex mutex;
19262306a36Sopenharmony_ci	unsigned width;
19362306a36Sopenharmony_ci	unsigned fmask;
19462306a36Sopenharmony_ci	unsigned fshift;
19562306a36Sopenharmony_ci	unsigned foff;
19662306a36Sopenharmony_ci	unsigned fmax;
19762306a36Sopenharmony_ci	bool bits_per_mux;
19862306a36Sopenharmony_ci	unsigned bits_per_pin;
19962306a36Sopenharmony_ci	struct pcs_data pins;
20062306a36Sopenharmony_ci	struct list_head gpiofuncs;
20162306a36Sopenharmony_ci	struct list_head irqs;
20262306a36Sopenharmony_ci	struct irq_chip chip;
20362306a36Sopenharmony_ci	struct irq_domain *domain;
20462306a36Sopenharmony_ci	struct pinctrl_desc desc;
20562306a36Sopenharmony_ci	unsigned (*read)(void __iomem *reg);
20662306a36Sopenharmony_ci	void (*write)(unsigned val, void __iomem *reg);
20762306a36Sopenharmony_ci};
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci#define PCS_QUIRK_HAS_SHARED_IRQ	(pcs->flags & PCS_QUIRK_SHARED_IRQ)
21062306a36Sopenharmony_ci#define PCS_HAS_IRQ		(pcs->flags & PCS_FEAT_IRQ)
21162306a36Sopenharmony_ci#define PCS_HAS_PINCONF		(pcs->flags & PCS_FEAT_PINCONF)
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int pcs_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin,
21462306a36Sopenharmony_ci			   unsigned long *config);
21562306a36Sopenharmony_cistatic int pcs_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin,
21662306a36Sopenharmony_ci			   unsigned long *configs, unsigned num_configs);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic enum pin_config_param pcs_bias[] = {
21962306a36Sopenharmony_ci	PIN_CONFIG_BIAS_PULL_DOWN,
22062306a36Sopenharmony_ci	PIN_CONFIG_BIAS_PULL_UP,
22162306a36Sopenharmony_ci};
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/*
22462306a36Sopenharmony_ci * This lock class tells lockdep that irqchip core that this single
22562306a36Sopenharmony_ci * pinctrl can be in a different category than its parents, so it won't
22662306a36Sopenharmony_ci * report false recursion.
22762306a36Sopenharmony_ci */
22862306a36Sopenharmony_cistatic struct lock_class_key pcs_lock_class;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/* Class for the IRQ request mutex */
23162306a36Sopenharmony_cistatic struct lock_class_key pcs_request_class;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * REVISIT: Reads and writes could eventually use regmap or something
23562306a36Sopenharmony_ci * generic. But at least on omaps, some mux registers are performance
23662306a36Sopenharmony_ci * critical as they may need to be remuxed every time before and after
23762306a36Sopenharmony_ci * idle. Adding tests for register access width for every read and
23862306a36Sopenharmony_ci * write like regmap is doing is not desired, and caching the registers
23962306a36Sopenharmony_ci * does not help in this case.
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic unsigned __maybe_unused pcs_readb(void __iomem *reg)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	return readb(reg);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic unsigned __maybe_unused pcs_readw(void __iomem *reg)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	return readw(reg);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic unsigned __maybe_unused pcs_readl(void __iomem *reg)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	return readl(reg);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void __maybe_unused pcs_writeb(unsigned val, void __iomem *reg)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	writeb(val, reg);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void __maybe_unused pcs_writew(unsigned val, void __iomem *reg)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	writew(val, reg);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void __maybe_unused pcs_writel(unsigned val, void __iomem *reg)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	writel(val, reg);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic unsigned int pcs_pin_reg_offset_get(struct pcs_device *pcs,
27362306a36Sopenharmony_ci					   unsigned int pin)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	unsigned int mux_bytes = pcs->width / BITS_PER_BYTE;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (pcs->bits_per_mux) {
27862306a36Sopenharmony_ci		unsigned int pin_offset_bytes;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		pin_offset_bytes = (pcs->bits_per_pin * pin) / BITS_PER_BYTE;
28162306a36Sopenharmony_ci		return (pin_offset_bytes / mux_bytes) * mux_bytes;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return pin * mux_bytes;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic unsigned int pcs_pin_shift_reg_get(struct pcs_device *pcs,
28862306a36Sopenharmony_ci					  unsigned int pin)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	return (pin % (pcs->width / pcs->bits_per_pin)) * pcs->bits_per_pin;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void pcs_pin_dbg_show(struct pinctrl_dev *pctldev,
29462306a36Sopenharmony_ci					struct seq_file *s,
29562306a36Sopenharmony_ci					unsigned pin)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct pcs_device *pcs;
29862306a36Sopenharmony_ci	unsigned int val;
29962306a36Sopenharmony_ci	unsigned long offset;
30062306a36Sopenharmony_ci	size_t pa;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	pcs = pinctrl_dev_get_drvdata(pctldev);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	offset = pcs_pin_reg_offset_get(pcs, pin);
30562306a36Sopenharmony_ci	val = pcs->read(pcs->base + offset);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (pcs->bits_per_mux)
30862306a36Sopenharmony_ci		val &= pcs->fmask << pcs_pin_shift_reg_get(pcs, pin);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	pa = pcs->res->start + offset;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	seq_printf(s, "%zx %08x %s ", pa, val, DRIVER_NAME);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void pcs_dt_free_map(struct pinctrl_dev *pctldev,
31662306a36Sopenharmony_ci				struct pinctrl_map *map, unsigned num_maps)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct pcs_device *pcs;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	pcs = pinctrl_dev_get_drvdata(pctldev);
32162306a36Sopenharmony_ci	devm_kfree(pcs->dev, map);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
32562306a36Sopenharmony_ci				struct device_node *np_config,
32662306a36Sopenharmony_ci				struct pinctrl_map **map, unsigned *num_maps);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic const struct pinctrl_ops pcs_pinctrl_ops = {
32962306a36Sopenharmony_ci	.get_groups_count = pinctrl_generic_get_group_count,
33062306a36Sopenharmony_ci	.get_group_name = pinctrl_generic_get_group_name,
33162306a36Sopenharmony_ci	.get_group_pins = pinctrl_generic_get_group_pins,
33262306a36Sopenharmony_ci	.pin_dbg_show = pcs_pin_dbg_show,
33362306a36Sopenharmony_ci	.dt_node_to_map = pcs_dt_node_to_map,
33462306a36Sopenharmony_ci	.dt_free_map = pcs_dt_free_map,
33562306a36Sopenharmony_ci};
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int pcs_get_function(struct pinctrl_dev *pctldev, unsigned pin,
33862306a36Sopenharmony_ci			    struct pcs_function **func)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
34162306a36Sopenharmony_ci	struct pin_desc *pdesc = pin_desc_get(pctldev, pin);
34262306a36Sopenharmony_ci	const struct pinctrl_setting_mux *setting;
34362306a36Sopenharmony_ci	struct function_desc *function;
34462306a36Sopenharmony_ci	unsigned fselector;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* If pin is not described in DTS & enabled, mux_setting is NULL. */
34762306a36Sopenharmony_ci	setting = pdesc->mux_setting;
34862306a36Sopenharmony_ci	if (!setting)
34962306a36Sopenharmony_ci		return -ENOTSUPP;
35062306a36Sopenharmony_ci	fselector = setting->func;
35162306a36Sopenharmony_ci	function = pinmux_generic_get_function(pctldev, fselector);
35262306a36Sopenharmony_ci	*func = function->data;
35362306a36Sopenharmony_ci	if (!(*func)) {
35462306a36Sopenharmony_ci		dev_err(pcs->dev, "%s could not find function%i\n",
35562306a36Sopenharmony_ci			__func__, fselector);
35662306a36Sopenharmony_ci		return -ENOTSUPP;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector,
36262306a36Sopenharmony_ci	unsigned group)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct pcs_device *pcs;
36562306a36Sopenharmony_ci	struct function_desc *function;
36662306a36Sopenharmony_ci	struct pcs_function *func;
36762306a36Sopenharmony_ci	int i;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	pcs = pinctrl_dev_get_drvdata(pctldev);
37062306a36Sopenharmony_ci	/* If function mask is null, needn't enable it. */
37162306a36Sopenharmony_ci	if (!pcs->fmask)
37262306a36Sopenharmony_ci		return 0;
37362306a36Sopenharmony_ci	function = pinmux_generic_get_function(pctldev, fselector);
37462306a36Sopenharmony_ci	if (!function)
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci	func = function->data;
37762306a36Sopenharmony_ci	if (!func)
37862306a36Sopenharmony_ci		return -EINVAL;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	dev_dbg(pcs->dev, "enabling %s function%i\n",
38162306a36Sopenharmony_ci		func->name, fselector);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	for (i = 0; i < func->nvals; i++) {
38462306a36Sopenharmony_ci		struct pcs_func_vals *vals;
38562306a36Sopenharmony_ci		unsigned long flags;
38662306a36Sopenharmony_ci		unsigned val, mask;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		vals = &func->vals[i];
38962306a36Sopenharmony_ci		raw_spin_lock_irqsave(&pcs->lock, flags);
39062306a36Sopenharmony_ci		val = pcs->read(vals->reg);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		if (pcs->bits_per_mux)
39362306a36Sopenharmony_ci			mask = vals->mask;
39462306a36Sopenharmony_ci		else
39562306a36Sopenharmony_ci			mask = pcs->fmask;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		val &= ~mask;
39862306a36Sopenharmony_ci		val |= (vals->val & mask);
39962306a36Sopenharmony_ci		pcs->write(val, vals->reg);
40062306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&pcs->lock, flags);
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return 0;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int pcs_request_gpio(struct pinctrl_dev *pctldev,
40762306a36Sopenharmony_ci			    struct pinctrl_gpio_range *range, unsigned pin)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
41062306a36Sopenharmony_ci	struct pcs_gpiofunc_range *frange = NULL;
41162306a36Sopenharmony_ci	struct list_head *pos, *tmp;
41262306a36Sopenharmony_ci	unsigned data;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* If function mask is null, return directly. */
41562306a36Sopenharmony_ci	if (!pcs->fmask)
41662306a36Sopenharmony_ci		return -ENOTSUPP;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	list_for_each_safe(pos, tmp, &pcs->gpiofuncs) {
41962306a36Sopenharmony_ci		u32 offset;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		frange = list_entry(pos, struct pcs_gpiofunc_range, node);
42262306a36Sopenharmony_ci		if (pin >= frange->offset + frange->npins
42362306a36Sopenharmony_ci			|| pin < frange->offset)
42462306a36Sopenharmony_ci			continue;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		offset = pcs_pin_reg_offset_get(pcs, pin);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		if (pcs->bits_per_mux) {
42962306a36Sopenharmony_ci			int pin_shift = pcs_pin_shift_reg_get(pcs, pin);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci			data = pcs->read(pcs->base + offset);
43262306a36Sopenharmony_ci			data &= ~(pcs->fmask << pin_shift);
43362306a36Sopenharmony_ci			data |= frange->gpiofunc << pin_shift;
43462306a36Sopenharmony_ci			pcs->write(data, pcs->base + offset);
43562306a36Sopenharmony_ci		} else {
43662306a36Sopenharmony_ci			data = pcs->read(pcs->base + offset);
43762306a36Sopenharmony_ci			data &= ~pcs->fmask;
43862306a36Sopenharmony_ci			data |= frange->gpiofunc;
43962306a36Sopenharmony_ci			pcs->write(data, pcs->base + offset);
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	return 0;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic const struct pinmux_ops pcs_pinmux_ops = {
44762306a36Sopenharmony_ci	.get_functions_count = pinmux_generic_get_function_count,
44862306a36Sopenharmony_ci	.get_function_name = pinmux_generic_get_function_name,
44962306a36Sopenharmony_ci	.get_function_groups = pinmux_generic_get_function_groups,
45062306a36Sopenharmony_ci	.set_mux = pcs_set_mux,
45162306a36Sopenharmony_ci	.gpio_request_enable = pcs_request_gpio,
45262306a36Sopenharmony_ci};
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/* Clear BIAS value */
45562306a36Sopenharmony_cistatic void pcs_pinconf_clear_bias(struct pinctrl_dev *pctldev, unsigned pin)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	unsigned long config;
45862306a36Sopenharmony_ci	int i;
45962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pcs_bias); i++) {
46062306a36Sopenharmony_ci		config = pinconf_to_config_packed(pcs_bias[i], 0);
46162306a36Sopenharmony_ci		pcs_pinconf_set(pctldev, pin, &config, 1);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci/*
46662306a36Sopenharmony_ci * Check whether PIN_CONFIG_BIAS_DISABLE is valid.
46762306a36Sopenharmony_ci * It's depend on that PULL_DOWN & PULL_UP configs are all invalid.
46862306a36Sopenharmony_ci */
46962306a36Sopenharmony_cistatic bool pcs_pinconf_bias_disable(struct pinctrl_dev *pctldev, unsigned pin)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	unsigned long config;
47262306a36Sopenharmony_ci	int i;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pcs_bias); i++) {
47562306a36Sopenharmony_ci		config = pinconf_to_config_packed(pcs_bias[i], 0);
47662306a36Sopenharmony_ci		if (!pcs_pinconf_get(pctldev, pin, &config))
47762306a36Sopenharmony_ci			goto out;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	return true;
48062306a36Sopenharmony_ciout:
48162306a36Sopenharmony_ci	return false;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int pcs_pinconf_get(struct pinctrl_dev *pctldev,
48562306a36Sopenharmony_ci				unsigned pin, unsigned long *config)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
48862306a36Sopenharmony_ci	struct pcs_function *func;
48962306a36Sopenharmony_ci	enum pin_config_param param;
49062306a36Sopenharmony_ci	unsigned offset = 0, data = 0, i, j, ret;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = pcs_get_function(pctldev, pin, &func);
49362306a36Sopenharmony_ci	if (ret)
49462306a36Sopenharmony_ci		return ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	for (i = 0; i < func->nconfs; i++) {
49762306a36Sopenharmony_ci		param = pinconf_to_config_param(*config);
49862306a36Sopenharmony_ci		if (param == PIN_CONFIG_BIAS_DISABLE) {
49962306a36Sopenharmony_ci			if (pcs_pinconf_bias_disable(pctldev, pin)) {
50062306a36Sopenharmony_ci				*config = 0;
50162306a36Sopenharmony_ci				return 0;
50262306a36Sopenharmony_ci			} else {
50362306a36Sopenharmony_ci				return -ENOTSUPP;
50462306a36Sopenharmony_ci			}
50562306a36Sopenharmony_ci		} else if (param != func->conf[i].param) {
50662306a36Sopenharmony_ci			continue;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		offset = pin * (pcs->width / BITS_PER_BYTE);
51062306a36Sopenharmony_ci		data = pcs->read(pcs->base + offset) & func->conf[i].mask;
51162306a36Sopenharmony_ci		switch (func->conf[i].param) {
51262306a36Sopenharmony_ci		/* 4 parameters */
51362306a36Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_DOWN:
51462306a36Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_UP:
51562306a36Sopenharmony_ci		case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
51662306a36Sopenharmony_ci			if ((data != func->conf[i].enable) ||
51762306a36Sopenharmony_ci			    (data == func->conf[i].disable))
51862306a36Sopenharmony_ci				return -ENOTSUPP;
51962306a36Sopenharmony_ci			*config = 0;
52062306a36Sopenharmony_ci			break;
52162306a36Sopenharmony_ci		/* 2 parameters */
52262306a36Sopenharmony_ci		case PIN_CONFIG_INPUT_SCHMITT:
52362306a36Sopenharmony_ci			for (j = 0; j < func->nconfs; j++) {
52462306a36Sopenharmony_ci				switch (func->conf[j].param) {
52562306a36Sopenharmony_ci				case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
52662306a36Sopenharmony_ci					if (data != func->conf[j].enable)
52762306a36Sopenharmony_ci						return -ENOTSUPP;
52862306a36Sopenharmony_ci					break;
52962306a36Sopenharmony_ci				default:
53062306a36Sopenharmony_ci					break;
53162306a36Sopenharmony_ci				}
53262306a36Sopenharmony_ci			}
53362306a36Sopenharmony_ci			*config = data;
53462306a36Sopenharmony_ci			break;
53562306a36Sopenharmony_ci		case PIN_CONFIG_DRIVE_STRENGTH:
53662306a36Sopenharmony_ci		case PIN_CONFIG_SLEW_RATE:
53762306a36Sopenharmony_ci		case PIN_CONFIG_MODE_LOW_POWER:
53862306a36Sopenharmony_ci		case PIN_CONFIG_INPUT_ENABLE:
53962306a36Sopenharmony_ci		default:
54062306a36Sopenharmony_ci			*config = data;
54162306a36Sopenharmony_ci			break;
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci		return 0;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	return -ENOTSUPP;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int pcs_pinconf_set(struct pinctrl_dev *pctldev,
54962306a36Sopenharmony_ci				unsigned pin, unsigned long *configs,
55062306a36Sopenharmony_ci				unsigned num_configs)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
55362306a36Sopenharmony_ci	struct pcs_function *func;
55462306a36Sopenharmony_ci	unsigned offset = 0, shift = 0, i, data, ret;
55562306a36Sopenharmony_ci	u32 arg;
55662306a36Sopenharmony_ci	int j;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	ret = pcs_get_function(pctldev, pin, &func);
55962306a36Sopenharmony_ci	if (ret)
56062306a36Sopenharmony_ci		return ret;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	for (j = 0; j < num_configs; j++) {
56362306a36Sopenharmony_ci		for (i = 0; i < func->nconfs; i++) {
56462306a36Sopenharmony_ci			if (pinconf_to_config_param(configs[j])
56562306a36Sopenharmony_ci				!= func->conf[i].param)
56662306a36Sopenharmony_ci				continue;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			offset = pin * (pcs->width / BITS_PER_BYTE);
56962306a36Sopenharmony_ci			data = pcs->read(pcs->base + offset);
57062306a36Sopenharmony_ci			arg = pinconf_to_config_argument(configs[j]);
57162306a36Sopenharmony_ci			switch (func->conf[i].param) {
57262306a36Sopenharmony_ci			/* 2 parameters */
57362306a36Sopenharmony_ci			case PIN_CONFIG_INPUT_SCHMITT:
57462306a36Sopenharmony_ci			case PIN_CONFIG_DRIVE_STRENGTH:
57562306a36Sopenharmony_ci			case PIN_CONFIG_SLEW_RATE:
57662306a36Sopenharmony_ci			case PIN_CONFIG_MODE_LOW_POWER:
57762306a36Sopenharmony_ci			case PIN_CONFIG_INPUT_ENABLE:
57862306a36Sopenharmony_ci				shift = ffs(func->conf[i].mask) - 1;
57962306a36Sopenharmony_ci				data &= ~func->conf[i].mask;
58062306a36Sopenharmony_ci				data |= (arg << shift) & func->conf[i].mask;
58162306a36Sopenharmony_ci				break;
58262306a36Sopenharmony_ci			/* 4 parameters */
58362306a36Sopenharmony_ci			case PIN_CONFIG_BIAS_DISABLE:
58462306a36Sopenharmony_ci				pcs_pinconf_clear_bias(pctldev, pin);
58562306a36Sopenharmony_ci				break;
58662306a36Sopenharmony_ci			case PIN_CONFIG_BIAS_PULL_DOWN:
58762306a36Sopenharmony_ci			case PIN_CONFIG_BIAS_PULL_UP:
58862306a36Sopenharmony_ci				if (arg)
58962306a36Sopenharmony_ci					pcs_pinconf_clear_bias(pctldev, pin);
59062306a36Sopenharmony_ci				fallthrough;
59162306a36Sopenharmony_ci			case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
59262306a36Sopenharmony_ci				data &= ~func->conf[i].mask;
59362306a36Sopenharmony_ci				if (arg)
59462306a36Sopenharmony_ci					data |= func->conf[i].enable;
59562306a36Sopenharmony_ci				else
59662306a36Sopenharmony_ci					data |= func->conf[i].disable;
59762306a36Sopenharmony_ci				break;
59862306a36Sopenharmony_ci			default:
59962306a36Sopenharmony_ci				return -ENOTSUPP;
60062306a36Sopenharmony_ci			}
60162306a36Sopenharmony_ci			pcs->write(data, pcs->base + offset);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci			break;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci		if (i >= func->nconfs)
60662306a36Sopenharmony_ci			return -ENOTSUPP;
60762306a36Sopenharmony_ci	} /* for each config */
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return 0;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int pcs_pinconf_group_get(struct pinctrl_dev *pctldev,
61362306a36Sopenharmony_ci				unsigned group, unsigned long *config)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	const unsigned *pins;
61662306a36Sopenharmony_ci	unsigned npins, old = 0;
61762306a36Sopenharmony_ci	int i, ret;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
62062306a36Sopenharmony_ci	if (ret)
62162306a36Sopenharmony_ci		return ret;
62262306a36Sopenharmony_ci	for (i = 0; i < npins; i++) {
62362306a36Sopenharmony_ci		if (pcs_pinconf_get(pctldev, pins[i], config))
62462306a36Sopenharmony_ci			return -ENOTSUPP;
62562306a36Sopenharmony_ci		/* configs do not match between two pins */
62662306a36Sopenharmony_ci		if (i && (old != *config))
62762306a36Sopenharmony_ci			return -ENOTSUPP;
62862306a36Sopenharmony_ci		old = *config;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci	return 0;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic int pcs_pinconf_group_set(struct pinctrl_dev *pctldev,
63462306a36Sopenharmony_ci				unsigned group, unsigned long *configs,
63562306a36Sopenharmony_ci				unsigned num_configs)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	const unsigned *pins;
63862306a36Sopenharmony_ci	unsigned npins;
63962306a36Sopenharmony_ci	int i, ret;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
64262306a36Sopenharmony_ci	if (ret)
64362306a36Sopenharmony_ci		return ret;
64462306a36Sopenharmony_ci	for (i = 0; i < npins; i++) {
64562306a36Sopenharmony_ci		if (pcs_pinconf_set(pctldev, pins[i], configs, num_configs))
64662306a36Sopenharmony_ci			return -ENOTSUPP;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci	return 0;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev,
65262306a36Sopenharmony_ci				struct seq_file *s, unsigned pin)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic void pcs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
65762306a36Sopenharmony_ci				struct seq_file *s, unsigned selector)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic void pcs_pinconf_config_dbg_show(struct pinctrl_dev *pctldev,
66262306a36Sopenharmony_ci					struct seq_file *s,
66362306a36Sopenharmony_ci					unsigned long config)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	pinconf_generic_dump_config(pctldev, s, config);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic const struct pinconf_ops pcs_pinconf_ops = {
66962306a36Sopenharmony_ci	.pin_config_get = pcs_pinconf_get,
67062306a36Sopenharmony_ci	.pin_config_set = pcs_pinconf_set,
67162306a36Sopenharmony_ci	.pin_config_group_get = pcs_pinconf_group_get,
67262306a36Sopenharmony_ci	.pin_config_group_set = pcs_pinconf_group_set,
67362306a36Sopenharmony_ci	.pin_config_dbg_show = pcs_pinconf_dbg_show,
67462306a36Sopenharmony_ci	.pin_config_group_dbg_show = pcs_pinconf_group_dbg_show,
67562306a36Sopenharmony_ci	.pin_config_config_dbg_show = pcs_pinconf_config_dbg_show,
67662306a36Sopenharmony_ci	.is_generic = true,
67762306a36Sopenharmony_ci};
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/**
68062306a36Sopenharmony_ci * pcs_add_pin() - add a pin to the static per controller pin array
68162306a36Sopenharmony_ci * @pcs: pcs driver instance
68262306a36Sopenharmony_ci * @offset: register offset from base
68362306a36Sopenharmony_ci */
68462306a36Sopenharmony_cistatic int pcs_add_pin(struct pcs_device *pcs, unsigned int offset)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = &pcs->socdata;
68762306a36Sopenharmony_ci	struct pinctrl_pin_desc *pin;
68862306a36Sopenharmony_ci	int i;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	i = pcs->pins.cur;
69162306a36Sopenharmony_ci	if (i >= pcs->desc.npins) {
69262306a36Sopenharmony_ci		dev_err(pcs->dev, "too many pins, max %i\n",
69362306a36Sopenharmony_ci			pcs->desc.npins);
69462306a36Sopenharmony_ci		return -ENOMEM;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (pcs_soc->irq_enable_mask) {
69862306a36Sopenharmony_ci		unsigned val;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		val = pcs->read(pcs->base + offset);
70162306a36Sopenharmony_ci		if (val & pcs_soc->irq_enable_mask) {
70262306a36Sopenharmony_ci			dev_dbg(pcs->dev, "irq enabled at boot for pin at %lx (%x), clearing\n",
70362306a36Sopenharmony_ci				(unsigned long)pcs->res->start + offset, val);
70462306a36Sopenharmony_ci			val &= ~pcs_soc->irq_enable_mask;
70562306a36Sopenharmony_ci			pcs->write(val, pcs->base + offset);
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	pin = &pcs->pins.pa[i];
71062306a36Sopenharmony_ci	pin->number = i;
71162306a36Sopenharmony_ci	pcs->pins.cur++;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return i;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci/**
71762306a36Sopenharmony_ci * pcs_allocate_pin_table() - adds all the pins for the pinctrl driver
71862306a36Sopenharmony_ci * @pcs: pcs driver instance
71962306a36Sopenharmony_ci *
72062306a36Sopenharmony_ci * In case of errors, resources are freed in pcs_free_resources.
72162306a36Sopenharmony_ci *
72262306a36Sopenharmony_ci * If your hardware needs holes in the address space, then just set
72362306a36Sopenharmony_ci * up multiple driver instances.
72462306a36Sopenharmony_ci */
72562306a36Sopenharmony_cistatic int pcs_allocate_pin_table(struct pcs_device *pcs)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	int mux_bytes, nr_pins, i;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	mux_bytes = pcs->width / BITS_PER_BYTE;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (pcs->bits_per_mux && pcs->fmask) {
73262306a36Sopenharmony_ci		pcs->bits_per_pin = fls(pcs->fmask);
73362306a36Sopenharmony_ci		nr_pins = (pcs->size * BITS_PER_BYTE) / pcs->bits_per_pin;
73462306a36Sopenharmony_ci	} else {
73562306a36Sopenharmony_ci		nr_pins = pcs->size / mux_bytes;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins);
73962306a36Sopenharmony_ci	pcs->pins.pa = devm_kcalloc(pcs->dev,
74062306a36Sopenharmony_ci				nr_pins, sizeof(*pcs->pins.pa),
74162306a36Sopenharmony_ci				GFP_KERNEL);
74262306a36Sopenharmony_ci	if (!pcs->pins.pa)
74362306a36Sopenharmony_ci		return -ENOMEM;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	pcs->desc.pins = pcs->pins.pa;
74662306a36Sopenharmony_ci	pcs->desc.npins = nr_pins;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	for (i = 0; i < pcs->desc.npins; i++) {
74962306a36Sopenharmony_ci		unsigned offset;
75062306a36Sopenharmony_ci		int res;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci		offset = pcs_pin_reg_offset_get(pcs, i);
75362306a36Sopenharmony_ci		res = pcs_add_pin(pcs, offset);
75462306a36Sopenharmony_ci		if (res < 0) {
75562306a36Sopenharmony_ci			dev_err(pcs->dev, "error adding pins: %i\n", res);
75662306a36Sopenharmony_ci			return res;
75762306a36Sopenharmony_ci		}
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	return 0;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/**
76462306a36Sopenharmony_ci * pcs_add_function() - adds a new function to the function list
76562306a36Sopenharmony_ci * @pcs: pcs driver instance
76662306a36Sopenharmony_ci * @fcn: new function allocated
76762306a36Sopenharmony_ci * @name: name of the function
76862306a36Sopenharmony_ci * @vals: array of mux register value pairs used by the function
76962306a36Sopenharmony_ci * @nvals: number of mux register value pairs
77062306a36Sopenharmony_ci * @pgnames: array of pingroup names for the function
77162306a36Sopenharmony_ci * @npgnames: number of pingroup names
77262306a36Sopenharmony_ci *
77362306a36Sopenharmony_ci * Caller must take care of locking.
77462306a36Sopenharmony_ci */
77562306a36Sopenharmony_cistatic int pcs_add_function(struct pcs_device *pcs,
77662306a36Sopenharmony_ci			    struct pcs_function **fcn,
77762306a36Sopenharmony_ci			    const char *name,
77862306a36Sopenharmony_ci			    struct pcs_func_vals *vals,
77962306a36Sopenharmony_ci			    unsigned int nvals,
78062306a36Sopenharmony_ci			    const char **pgnames,
78162306a36Sopenharmony_ci			    unsigned int npgnames)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct pcs_function *function;
78462306a36Sopenharmony_ci	int selector;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	function = devm_kzalloc(pcs->dev, sizeof(*function), GFP_KERNEL);
78762306a36Sopenharmony_ci	if (!function)
78862306a36Sopenharmony_ci		return -ENOMEM;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	function->vals = vals;
79162306a36Sopenharmony_ci	function->nvals = nvals;
79262306a36Sopenharmony_ci	function->name = name;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	selector = pinmux_generic_add_function(pcs->pctl, name,
79562306a36Sopenharmony_ci					       pgnames, npgnames,
79662306a36Sopenharmony_ci					       function);
79762306a36Sopenharmony_ci	if (selector < 0) {
79862306a36Sopenharmony_ci		devm_kfree(pcs->dev, function);
79962306a36Sopenharmony_ci		*fcn = NULL;
80062306a36Sopenharmony_ci	} else {
80162306a36Sopenharmony_ci		*fcn = function;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return selector;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci/**
80862306a36Sopenharmony_ci * pcs_get_pin_by_offset() - get a pin index based on the register offset
80962306a36Sopenharmony_ci * @pcs: pcs driver instance
81062306a36Sopenharmony_ci * @offset: register offset from the base
81162306a36Sopenharmony_ci *
81262306a36Sopenharmony_ci * Note that this is OK as long as the pins are in a static array.
81362306a36Sopenharmony_ci */
81462306a36Sopenharmony_cistatic int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	unsigned index;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (offset >= pcs->size) {
81962306a36Sopenharmony_ci		dev_err(pcs->dev, "mux offset out of range: 0x%x (0x%x)\n",
82062306a36Sopenharmony_ci			offset, pcs->size);
82162306a36Sopenharmony_ci		return -EINVAL;
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (pcs->bits_per_mux)
82562306a36Sopenharmony_ci		index = (offset * BITS_PER_BYTE) / pcs->bits_per_pin;
82662306a36Sopenharmony_ci	else
82762306a36Sopenharmony_ci		index = offset / (pcs->width / BITS_PER_BYTE);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	return index;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/*
83362306a36Sopenharmony_ci * check whether data matches enable bits or disable bits
83462306a36Sopenharmony_ci * Return value: 1 for matching enable bits, 0 for matching disable bits,
83562306a36Sopenharmony_ci *               and negative value for matching failure.
83662306a36Sopenharmony_ci */
83762306a36Sopenharmony_cistatic int pcs_config_match(unsigned data, unsigned enable, unsigned disable)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	int ret = -EINVAL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (data == enable)
84262306a36Sopenharmony_ci		ret = 1;
84362306a36Sopenharmony_ci	else if (data == disable)
84462306a36Sopenharmony_ci		ret = 0;
84562306a36Sopenharmony_ci	return ret;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic void add_config(struct pcs_conf_vals **conf, enum pin_config_param param,
84962306a36Sopenharmony_ci		       unsigned value, unsigned enable, unsigned disable,
85062306a36Sopenharmony_ci		       unsigned mask)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	(*conf)->param = param;
85362306a36Sopenharmony_ci	(*conf)->val = value;
85462306a36Sopenharmony_ci	(*conf)->enable = enable;
85562306a36Sopenharmony_ci	(*conf)->disable = disable;
85662306a36Sopenharmony_ci	(*conf)->mask = mask;
85762306a36Sopenharmony_ci	(*conf)++;
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic void add_setting(unsigned long **setting, enum pin_config_param param,
86162306a36Sopenharmony_ci			unsigned arg)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	**setting = pinconf_to_config_packed(param, arg);
86462306a36Sopenharmony_ci	(*setting)++;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/* add pinconf setting with 2 parameters */
86862306a36Sopenharmony_cistatic void pcs_add_conf2(struct pcs_device *pcs, struct device_node *np,
86962306a36Sopenharmony_ci			  const char *name, enum pin_config_param param,
87062306a36Sopenharmony_ci			  struct pcs_conf_vals **conf, unsigned long **settings)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	unsigned value[2], shift;
87362306a36Sopenharmony_ci	int ret;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ret = of_property_read_u32_array(np, name, value, 2);
87662306a36Sopenharmony_ci	if (ret)
87762306a36Sopenharmony_ci		return;
87862306a36Sopenharmony_ci	/* set value & mask */
87962306a36Sopenharmony_ci	value[0] &= value[1];
88062306a36Sopenharmony_ci	shift = ffs(value[1]) - 1;
88162306a36Sopenharmony_ci	/* skip enable & disable */
88262306a36Sopenharmony_ci	add_config(conf, param, value[0], 0, 0, value[1]);
88362306a36Sopenharmony_ci	add_setting(settings, param, value[0] >> shift);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/* add pinconf setting with 4 parameters */
88762306a36Sopenharmony_cistatic void pcs_add_conf4(struct pcs_device *pcs, struct device_node *np,
88862306a36Sopenharmony_ci			  const char *name, enum pin_config_param param,
88962306a36Sopenharmony_ci			  struct pcs_conf_vals **conf, unsigned long **settings)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	unsigned value[4];
89262306a36Sopenharmony_ci	int ret;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* value to set, enable, disable, mask */
89562306a36Sopenharmony_ci	ret = of_property_read_u32_array(np, name, value, 4);
89662306a36Sopenharmony_ci	if (ret)
89762306a36Sopenharmony_ci		return;
89862306a36Sopenharmony_ci	if (!value[3]) {
89962306a36Sopenharmony_ci		dev_err(pcs->dev, "mask field of the property can't be 0\n");
90062306a36Sopenharmony_ci		return;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci	value[0] &= value[3];
90362306a36Sopenharmony_ci	value[1] &= value[3];
90462306a36Sopenharmony_ci	value[2] &= value[3];
90562306a36Sopenharmony_ci	ret = pcs_config_match(value[0], value[1], value[2]);
90662306a36Sopenharmony_ci	if (ret < 0)
90762306a36Sopenharmony_ci		dev_dbg(pcs->dev, "failed to match enable or disable bits\n");
90862306a36Sopenharmony_ci	add_config(conf, param, value[0], value[1], value[2], value[3]);
90962306a36Sopenharmony_ci	add_setting(settings, param, ret);
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np,
91362306a36Sopenharmony_ci			     struct pcs_function *func,
91462306a36Sopenharmony_ci			     struct pinctrl_map **map)
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	struct pinctrl_map *m = *map;
91862306a36Sopenharmony_ci	int i = 0, nconfs = 0;
91962306a36Sopenharmony_ci	unsigned long *settings = NULL, *s = NULL;
92062306a36Sopenharmony_ci	struct pcs_conf_vals *conf = NULL;
92162306a36Sopenharmony_ci	static const struct pcs_conf_type prop2[] = {
92262306a36Sopenharmony_ci		{ "pinctrl-single,drive-strength", PIN_CONFIG_DRIVE_STRENGTH, },
92362306a36Sopenharmony_ci		{ "pinctrl-single,slew-rate", PIN_CONFIG_SLEW_RATE, },
92462306a36Sopenharmony_ci		{ "pinctrl-single,input-enable", PIN_CONFIG_INPUT_ENABLE, },
92562306a36Sopenharmony_ci		{ "pinctrl-single,input-schmitt", PIN_CONFIG_INPUT_SCHMITT, },
92662306a36Sopenharmony_ci		{ "pinctrl-single,low-power-mode", PIN_CONFIG_MODE_LOW_POWER, },
92762306a36Sopenharmony_ci	};
92862306a36Sopenharmony_ci	static const struct pcs_conf_type prop4[] = {
92962306a36Sopenharmony_ci		{ "pinctrl-single,bias-pullup", PIN_CONFIG_BIAS_PULL_UP, },
93062306a36Sopenharmony_ci		{ "pinctrl-single,bias-pulldown", PIN_CONFIG_BIAS_PULL_DOWN, },
93162306a36Sopenharmony_ci		{ "pinctrl-single,input-schmitt-enable",
93262306a36Sopenharmony_ci			PIN_CONFIG_INPUT_SCHMITT_ENABLE, },
93362306a36Sopenharmony_ci	};
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* If pinconf isn't supported, don't parse properties in below. */
93662306a36Sopenharmony_ci	if (!PCS_HAS_PINCONF)
93762306a36Sopenharmony_ci		return -ENOTSUPP;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* cacluate how much properties are supported in current node */
94062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(prop2); i++) {
94162306a36Sopenharmony_ci		if (of_property_present(np, prop2[i].name))
94262306a36Sopenharmony_ci			nconfs++;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(prop4); i++) {
94562306a36Sopenharmony_ci		if (of_property_present(np, prop4[i].name))
94662306a36Sopenharmony_ci			nconfs++;
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci	if (!nconfs)
94962306a36Sopenharmony_ci		return -ENOTSUPP;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	func->conf = devm_kcalloc(pcs->dev,
95262306a36Sopenharmony_ci				  nconfs, sizeof(struct pcs_conf_vals),
95362306a36Sopenharmony_ci				  GFP_KERNEL);
95462306a36Sopenharmony_ci	if (!func->conf)
95562306a36Sopenharmony_ci		return -ENOMEM;
95662306a36Sopenharmony_ci	func->nconfs = nconfs;
95762306a36Sopenharmony_ci	conf = &(func->conf[0]);
95862306a36Sopenharmony_ci	m++;
95962306a36Sopenharmony_ci	settings = devm_kcalloc(pcs->dev, nconfs, sizeof(unsigned long),
96062306a36Sopenharmony_ci				GFP_KERNEL);
96162306a36Sopenharmony_ci	if (!settings)
96262306a36Sopenharmony_ci		return -ENOMEM;
96362306a36Sopenharmony_ci	s = &settings[0];
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(prop2); i++)
96662306a36Sopenharmony_ci		pcs_add_conf2(pcs, np, prop2[i].name, prop2[i].param,
96762306a36Sopenharmony_ci			      &conf, &s);
96862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(prop4); i++)
96962306a36Sopenharmony_ci		pcs_add_conf4(pcs, np, prop4[i].name, prop4[i].param,
97062306a36Sopenharmony_ci			      &conf, &s);
97162306a36Sopenharmony_ci	m->type = PIN_MAP_TYPE_CONFIGS_GROUP;
97262306a36Sopenharmony_ci	m->data.configs.group_or_pin = np->name;
97362306a36Sopenharmony_ci	m->data.configs.configs = settings;
97462306a36Sopenharmony_ci	m->data.configs.num_configs = nconfs;
97562306a36Sopenharmony_ci	return 0;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci/**
97962306a36Sopenharmony_ci * pcs_parse_one_pinctrl_entry() - parses a device tree mux entry
98062306a36Sopenharmony_ci * @pcs: pinctrl driver instance
98162306a36Sopenharmony_ci * @np: device node of the mux entry
98262306a36Sopenharmony_ci * @map: map entry
98362306a36Sopenharmony_ci * @num_maps: number of map
98462306a36Sopenharmony_ci * @pgnames: pingroup names
98562306a36Sopenharmony_ci *
98662306a36Sopenharmony_ci * Note that this binding currently supports only sets of one register + value.
98762306a36Sopenharmony_ci *
98862306a36Sopenharmony_ci * Also note that this driver tries to avoid understanding pin and function
98962306a36Sopenharmony_ci * names because of the extra bloat they would cause especially in the case of
99062306a36Sopenharmony_ci * a large number of pins. This driver just sets what is specified for the board
99162306a36Sopenharmony_ci * in the .dts file. Further user space debugging tools can be developed to
99262306a36Sopenharmony_ci * decipher the pin and function names using debugfs.
99362306a36Sopenharmony_ci *
99462306a36Sopenharmony_ci * If you are concerned about the boot time, set up the static pins in
99562306a36Sopenharmony_ci * the bootloader, and only set up selected pins as device tree entries.
99662306a36Sopenharmony_ci */
99762306a36Sopenharmony_cistatic int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
99862306a36Sopenharmony_ci						struct device_node *np,
99962306a36Sopenharmony_ci						struct pinctrl_map **map,
100062306a36Sopenharmony_ci						unsigned *num_maps,
100162306a36Sopenharmony_ci						const char **pgnames)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	const char *name = "pinctrl-single,pins";
100462306a36Sopenharmony_ci	struct pcs_func_vals *vals;
100562306a36Sopenharmony_ci	int rows, *pins, found = 0, res = -ENOMEM, i, fsel, gsel;
100662306a36Sopenharmony_ci	struct pcs_function *function = NULL;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	rows = pinctrl_count_index_with_args(np, name);
100962306a36Sopenharmony_ci	if (rows <= 0) {
101062306a36Sopenharmony_ci		dev_err(pcs->dev, "Invalid number of rows: %d\n", rows);
101162306a36Sopenharmony_ci		return -EINVAL;
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	vals = devm_kcalloc(pcs->dev, rows, sizeof(*vals), GFP_KERNEL);
101562306a36Sopenharmony_ci	if (!vals)
101662306a36Sopenharmony_ci		return -ENOMEM;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	pins = devm_kcalloc(pcs->dev, rows, sizeof(*pins), GFP_KERNEL);
101962306a36Sopenharmony_ci	if (!pins)
102062306a36Sopenharmony_ci		goto free_vals;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	for (i = 0; i < rows; i++) {
102362306a36Sopenharmony_ci		struct of_phandle_args pinctrl_spec;
102462306a36Sopenharmony_ci		unsigned int offset;
102562306a36Sopenharmony_ci		int pin;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		res = pinctrl_parse_index_with_args(np, name, i, &pinctrl_spec);
102862306a36Sopenharmony_ci		if (res)
102962306a36Sopenharmony_ci			return res;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci		if (pinctrl_spec.args_count < 2 || pinctrl_spec.args_count > 3) {
103262306a36Sopenharmony_ci			dev_err(pcs->dev, "invalid args_count for spec: %i\n",
103362306a36Sopenharmony_ci				pinctrl_spec.args_count);
103462306a36Sopenharmony_ci			break;
103562306a36Sopenharmony_ci		}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci		offset = pinctrl_spec.args[0];
103862306a36Sopenharmony_ci		vals[found].reg = pcs->base + offset;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		switch (pinctrl_spec.args_count) {
104162306a36Sopenharmony_ci		case 2:
104262306a36Sopenharmony_ci			vals[found].val = pinctrl_spec.args[1];
104362306a36Sopenharmony_ci			break;
104462306a36Sopenharmony_ci		case 3:
104562306a36Sopenharmony_ci			vals[found].val = (pinctrl_spec.args[1] | pinctrl_spec.args[2]);
104662306a36Sopenharmony_ci			break;
104762306a36Sopenharmony_ci		}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci		dev_dbg(pcs->dev, "%pOFn index: 0x%x value: 0x%x\n",
105062306a36Sopenharmony_ci			pinctrl_spec.np, offset, vals[found].val);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		pin = pcs_get_pin_by_offset(pcs, offset);
105362306a36Sopenharmony_ci		if (pin < 0) {
105462306a36Sopenharmony_ci			dev_err(pcs->dev,
105562306a36Sopenharmony_ci				"could not add functions for %pOFn %ux\n",
105662306a36Sopenharmony_ci				np, offset);
105762306a36Sopenharmony_ci			break;
105862306a36Sopenharmony_ci		}
105962306a36Sopenharmony_ci		pins[found++] = pin;
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	pgnames[0] = np->name;
106362306a36Sopenharmony_ci	mutex_lock(&pcs->mutex);
106462306a36Sopenharmony_ci	fsel = pcs_add_function(pcs, &function, np->name, vals, found,
106562306a36Sopenharmony_ci				pgnames, 1);
106662306a36Sopenharmony_ci	if (fsel < 0) {
106762306a36Sopenharmony_ci		res = fsel;
106862306a36Sopenharmony_ci		goto free_pins;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	gsel = pinctrl_generic_add_group(pcs->pctl, np->name, pins, found, pcs);
107262306a36Sopenharmony_ci	if (gsel < 0) {
107362306a36Sopenharmony_ci		res = gsel;
107462306a36Sopenharmony_ci		goto free_function;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	(*map)->type = PIN_MAP_TYPE_MUX_GROUP;
107862306a36Sopenharmony_ci	(*map)->data.mux.group = np->name;
107962306a36Sopenharmony_ci	(*map)->data.mux.function = np->name;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (PCS_HAS_PINCONF && function) {
108262306a36Sopenharmony_ci		res = pcs_parse_pinconf(pcs, np, function, map);
108362306a36Sopenharmony_ci		if (res == 0)
108462306a36Sopenharmony_ci			*num_maps = 2;
108562306a36Sopenharmony_ci		else if (res == -ENOTSUPP)
108662306a36Sopenharmony_ci			*num_maps = 1;
108762306a36Sopenharmony_ci		else
108862306a36Sopenharmony_ci			goto free_pingroups;
108962306a36Sopenharmony_ci	} else {
109062306a36Sopenharmony_ci		*num_maps = 1;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci	mutex_unlock(&pcs->mutex);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	return 0;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cifree_pingroups:
109762306a36Sopenharmony_ci	pinctrl_generic_remove_group(pcs->pctl, gsel);
109862306a36Sopenharmony_ci	*num_maps = 1;
109962306a36Sopenharmony_cifree_function:
110062306a36Sopenharmony_ci	pinmux_generic_remove_function(pcs->pctl, fsel);
110162306a36Sopenharmony_cifree_pins:
110262306a36Sopenharmony_ci	mutex_unlock(&pcs->mutex);
110362306a36Sopenharmony_ci	devm_kfree(pcs->dev, pins);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cifree_vals:
110662306a36Sopenharmony_ci	devm_kfree(pcs->dev, vals);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	return res;
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_cistatic int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs,
111262306a36Sopenharmony_ci						struct device_node *np,
111362306a36Sopenharmony_ci						struct pinctrl_map **map,
111462306a36Sopenharmony_ci						unsigned *num_maps,
111562306a36Sopenharmony_ci						const char **pgnames)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	const char *name = "pinctrl-single,bits";
111862306a36Sopenharmony_ci	struct pcs_func_vals *vals;
111962306a36Sopenharmony_ci	int rows, *pins, found = 0, res = -ENOMEM, i, fsel;
112062306a36Sopenharmony_ci	int npins_in_row;
112162306a36Sopenharmony_ci	struct pcs_function *function = NULL;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	rows = pinctrl_count_index_with_args(np, name);
112462306a36Sopenharmony_ci	if (rows <= 0) {
112562306a36Sopenharmony_ci		dev_err(pcs->dev, "Invalid number of rows: %d\n", rows);
112662306a36Sopenharmony_ci		return -EINVAL;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (PCS_HAS_PINCONF) {
113062306a36Sopenharmony_ci		dev_err(pcs->dev, "pinconf not supported\n");
113162306a36Sopenharmony_ci		return -ENOTSUPP;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	npins_in_row = pcs->width / pcs->bits_per_pin;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	vals = devm_kzalloc(pcs->dev,
113762306a36Sopenharmony_ci			    array3_size(rows, npins_in_row, sizeof(*vals)),
113862306a36Sopenharmony_ci			    GFP_KERNEL);
113962306a36Sopenharmony_ci	if (!vals)
114062306a36Sopenharmony_ci		return -ENOMEM;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	pins = devm_kzalloc(pcs->dev,
114362306a36Sopenharmony_ci			    array3_size(rows, npins_in_row, sizeof(*pins)),
114462306a36Sopenharmony_ci			    GFP_KERNEL);
114562306a36Sopenharmony_ci	if (!pins)
114662306a36Sopenharmony_ci		goto free_vals;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	for (i = 0; i < rows; i++) {
114962306a36Sopenharmony_ci		struct of_phandle_args pinctrl_spec;
115062306a36Sopenharmony_ci		unsigned offset, val;
115162306a36Sopenharmony_ci		unsigned mask, bit_pos, val_pos, mask_pos, submask;
115262306a36Sopenharmony_ci		unsigned pin_num_from_lsb;
115362306a36Sopenharmony_ci		int pin;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci		res = pinctrl_parse_index_with_args(np, name, i, &pinctrl_spec);
115662306a36Sopenharmony_ci		if (res)
115762306a36Sopenharmony_ci			return res;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		if (pinctrl_spec.args_count < 3) {
116062306a36Sopenharmony_ci			dev_err(pcs->dev, "invalid args_count for spec: %i\n",
116162306a36Sopenharmony_ci				pinctrl_spec.args_count);
116262306a36Sopenharmony_ci			break;
116362306a36Sopenharmony_ci		}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		/* Index plus two value cells */
116662306a36Sopenharmony_ci		offset = pinctrl_spec.args[0];
116762306a36Sopenharmony_ci		val = pinctrl_spec.args[1];
116862306a36Sopenharmony_ci		mask = pinctrl_spec.args[2];
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci		dev_dbg(pcs->dev, "%pOFn index: 0x%x value: 0x%x mask: 0x%x\n",
117162306a36Sopenharmony_ci			pinctrl_spec.np, offset, val, mask);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		/* Parse pins in each row from LSB */
117462306a36Sopenharmony_ci		while (mask) {
117562306a36Sopenharmony_ci			bit_pos = __ffs(mask);
117662306a36Sopenharmony_ci			pin_num_from_lsb = bit_pos / pcs->bits_per_pin;
117762306a36Sopenharmony_ci			mask_pos = ((pcs->fmask) << bit_pos);
117862306a36Sopenharmony_ci			val_pos = val & mask_pos;
117962306a36Sopenharmony_ci			submask = mask & mask_pos;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci			if ((mask & mask_pos) == 0) {
118262306a36Sopenharmony_ci				dev_err(pcs->dev,
118362306a36Sopenharmony_ci					"Invalid mask for %pOFn at 0x%x\n",
118462306a36Sopenharmony_ci					np, offset);
118562306a36Sopenharmony_ci				break;
118662306a36Sopenharmony_ci			}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci			mask &= ~mask_pos;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci			if (submask != mask_pos) {
119162306a36Sopenharmony_ci				dev_warn(pcs->dev,
119262306a36Sopenharmony_ci						"Invalid submask 0x%x for %pOFn at 0x%x\n",
119362306a36Sopenharmony_ci						submask, np, offset);
119462306a36Sopenharmony_ci				continue;
119562306a36Sopenharmony_ci			}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci			vals[found].mask = submask;
119862306a36Sopenharmony_ci			vals[found].reg = pcs->base + offset;
119962306a36Sopenharmony_ci			vals[found].val = val_pos;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci			pin = pcs_get_pin_by_offset(pcs, offset);
120262306a36Sopenharmony_ci			if (pin < 0) {
120362306a36Sopenharmony_ci				dev_err(pcs->dev,
120462306a36Sopenharmony_ci					"could not add functions for %pOFn %ux\n",
120562306a36Sopenharmony_ci					np, offset);
120662306a36Sopenharmony_ci				break;
120762306a36Sopenharmony_ci			}
120862306a36Sopenharmony_ci			pins[found++] = pin + pin_num_from_lsb;
120962306a36Sopenharmony_ci		}
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	pgnames[0] = np->name;
121362306a36Sopenharmony_ci	mutex_lock(&pcs->mutex);
121462306a36Sopenharmony_ci	fsel = pcs_add_function(pcs, &function, np->name, vals, found,
121562306a36Sopenharmony_ci				pgnames, 1);
121662306a36Sopenharmony_ci	if (fsel < 0) {
121762306a36Sopenharmony_ci		res = fsel;
121862306a36Sopenharmony_ci		goto free_pins;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	res = pinctrl_generic_add_group(pcs->pctl, np->name, pins, found, pcs);
122262306a36Sopenharmony_ci	if (res < 0)
122362306a36Sopenharmony_ci		goto free_function;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	(*map)->type = PIN_MAP_TYPE_MUX_GROUP;
122662306a36Sopenharmony_ci	(*map)->data.mux.group = np->name;
122762306a36Sopenharmony_ci	(*map)->data.mux.function = np->name;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	*num_maps = 1;
123062306a36Sopenharmony_ci	mutex_unlock(&pcs->mutex);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	return 0;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cifree_function:
123562306a36Sopenharmony_ci	pinmux_generic_remove_function(pcs->pctl, fsel);
123662306a36Sopenharmony_cifree_pins:
123762306a36Sopenharmony_ci	mutex_unlock(&pcs->mutex);
123862306a36Sopenharmony_ci	devm_kfree(pcs->dev, pins);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cifree_vals:
124162306a36Sopenharmony_ci	devm_kfree(pcs->dev, vals);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	return res;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci/**
124662306a36Sopenharmony_ci * pcs_dt_node_to_map() - allocates and parses pinctrl maps
124762306a36Sopenharmony_ci * @pctldev: pinctrl instance
124862306a36Sopenharmony_ci * @np_config: device tree pinmux entry
124962306a36Sopenharmony_ci * @map: array of map entries
125062306a36Sopenharmony_ci * @num_maps: number of maps
125162306a36Sopenharmony_ci */
125262306a36Sopenharmony_cistatic int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
125362306a36Sopenharmony_ci				struct device_node *np_config,
125462306a36Sopenharmony_ci				struct pinctrl_map **map, unsigned *num_maps)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct pcs_device *pcs;
125762306a36Sopenharmony_ci	const char **pgnames;
125862306a36Sopenharmony_ci	int ret;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	pcs = pinctrl_dev_get_drvdata(pctldev);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* create 2 maps. One is for pinmux, and the other is for pinconf. */
126362306a36Sopenharmony_ci	*map = devm_kcalloc(pcs->dev, 2, sizeof(**map), GFP_KERNEL);
126462306a36Sopenharmony_ci	if (!*map)
126562306a36Sopenharmony_ci		return -ENOMEM;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	*num_maps = 0;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
127062306a36Sopenharmony_ci	if (!pgnames) {
127162306a36Sopenharmony_ci		ret = -ENOMEM;
127262306a36Sopenharmony_ci		goto free_map;
127362306a36Sopenharmony_ci	}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (pcs->bits_per_mux) {
127662306a36Sopenharmony_ci		ret = pcs_parse_bits_in_pinctrl_entry(pcs, np_config, map,
127762306a36Sopenharmony_ci				num_maps, pgnames);
127862306a36Sopenharmony_ci		if (ret < 0) {
127962306a36Sopenharmony_ci			dev_err(pcs->dev, "no pins entries for %pOFn\n",
128062306a36Sopenharmony_ci				np_config);
128162306a36Sopenharmony_ci			goto free_pgnames;
128262306a36Sopenharmony_ci		}
128362306a36Sopenharmony_ci	} else {
128462306a36Sopenharmony_ci		ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map,
128562306a36Sopenharmony_ci				num_maps, pgnames);
128662306a36Sopenharmony_ci		if (ret < 0) {
128762306a36Sopenharmony_ci			dev_err(pcs->dev, "no pins entries for %pOFn\n",
128862306a36Sopenharmony_ci				np_config);
128962306a36Sopenharmony_ci			goto free_pgnames;
129062306a36Sopenharmony_ci		}
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	return 0;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cifree_pgnames:
129662306a36Sopenharmony_ci	devm_kfree(pcs->dev, pgnames);
129762306a36Sopenharmony_cifree_map:
129862306a36Sopenharmony_ci	devm_kfree(pcs->dev, *map);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	return ret;
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci/**
130462306a36Sopenharmony_ci * pcs_irq_free() - free interrupt
130562306a36Sopenharmony_ci * @pcs: pcs driver instance
130662306a36Sopenharmony_ci */
130762306a36Sopenharmony_cistatic void pcs_irq_free(struct pcs_device *pcs)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = &pcs->socdata;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (pcs_soc->irq < 0)
131262306a36Sopenharmony_ci		return;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (pcs->domain)
131562306a36Sopenharmony_ci		irq_domain_remove(pcs->domain);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (PCS_QUIRK_HAS_SHARED_IRQ)
131862306a36Sopenharmony_ci		free_irq(pcs_soc->irq, pcs_soc);
131962306a36Sopenharmony_ci	else
132062306a36Sopenharmony_ci		irq_set_chained_handler(pcs_soc->irq, NULL);
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci/**
132462306a36Sopenharmony_ci * pcs_free_resources() - free memory used by this driver
132562306a36Sopenharmony_ci * @pcs: pcs driver instance
132662306a36Sopenharmony_ci */
132762306a36Sopenharmony_cistatic void pcs_free_resources(struct pcs_device *pcs)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	pcs_irq_free(pcs);
133062306a36Sopenharmony_ci	pinctrl_unregister(pcs->pctl);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_PINCTRL_SINGLE)
133362306a36Sopenharmony_ci	if (pcs->missing_nr_pinctrl_cells)
133462306a36Sopenharmony_ci		of_remove_property(pcs->np, pcs->missing_nr_pinctrl_cells);
133562306a36Sopenharmony_ci#endif
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_cistatic int pcs_add_gpio_func(struct device_node *node, struct pcs_device *pcs)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	const char *propname = "pinctrl-single,gpio-range";
134162306a36Sopenharmony_ci	const char *cellname = "#pinctrl-single,gpio-range-cells";
134262306a36Sopenharmony_ci	struct of_phandle_args gpiospec;
134362306a36Sopenharmony_ci	struct pcs_gpiofunc_range *range;
134462306a36Sopenharmony_ci	int ret, i;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	for (i = 0; ; i++) {
134762306a36Sopenharmony_ci		ret = of_parse_phandle_with_args(node, propname, cellname,
134862306a36Sopenharmony_ci						 i, &gpiospec);
134962306a36Sopenharmony_ci		/* Do not treat it as error. Only treat it as end condition. */
135062306a36Sopenharmony_ci		if (ret) {
135162306a36Sopenharmony_ci			ret = 0;
135262306a36Sopenharmony_ci			break;
135362306a36Sopenharmony_ci		}
135462306a36Sopenharmony_ci		range = devm_kzalloc(pcs->dev, sizeof(*range), GFP_KERNEL);
135562306a36Sopenharmony_ci		if (!range) {
135662306a36Sopenharmony_ci			ret = -ENOMEM;
135762306a36Sopenharmony_ci			break;
135862306a36Sopenharmony_ci		}
135962306a36Sopenharmony_ci		range->offset = gpiospec.args[0];
136062306a36Sopenharmony_ci		range->npins = gpiospec.args[1];
136162306a36Sopenharmony_ci		range->gpiofunc = gpiospec.args[2];
136262306a36Sopenharmony_ci		mutex_lock(&pcs->mutex);
136362306a36Sopenharmony_ci		list_add_tail(&range->node, &pcs->gpiofuncs);
136462306a36Sopenharmony_ci		mutex_unlock(&pcs->mutex);
136562306a36Sopenharmony_ci	}
136662306a36Sopenharmony_ci	return ret;
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci/**
137062306a36Sopenharmony_ci * struct pcs_interrupt
137162306a36Sopenharmony_ci * @reg:	virtual address of interrupt register
137262306a36Sopenharmony_ci * @hwirq:	hardware irq number
137362306a36Sopenharmony_ci * @irq:	virtual irq number
137462306a36Sopenharmony_ci * @node:	list node
137562306a36Sopenharmony_ci */
137662306a36Sopenharmony_cistruct pcs_interrupt {
137762306a36Sopenharmony_ci	void __iomem *reg;
137862306a36Sopenharmony_ci	irq_hw_number_t hwirq;
137962306a36Sopenharmony_ci	unsigned int irq;
138062306a36Sopenharmony_ci	struct list_head node;
138162306a36Sopenharmony_ci};
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci/**
138462306a36Sopenharmony_ci * pcs_irq_set() - enables or disables an interrupt
138562306a36Sopenharmony_ci * @pcs_soc: SoC specific settings
138662306a36Sopenharmony_ci * @irq: interrupt
138762306a36Sopenharmony_ci * @enable: enable or disable the interrupt
138862306a36Sopenharmony_ci *
138962306a36Sopenharmony_ci * Note that this currently assumes one interrupt per pinctrl
139062306a36Sopenharmony_ci * register that is typically used for wake-up events.
139162306a36Sopenharmony_ci */
139262306a36Sopenharmony_cistatic inline void pcs_irq_set(struct pcs_soc_data *pcs_soc,
139362306a36Sopenharmony_ci			       int irq, const bool enable)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	struct pcs_device *pcs;
139662306a36Sopenharmony_ci	struct list_head *pos;
139762306a36Sopenharmony_ci	unsigned mask;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	pcs = container_of(pcs_soc, struct pcs_device, socdata);
140062306a36Sopenharmony_ci	list_for_each(pos, &pcs->irqs) {
140162306a36Sopenharmony_ci		struct pcs_interrupt *pcswi;
140262306a36Sopenharmony_ci		unsigned soc_mask;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		pcswi = list_entry(pos, struct pcs_interrupt, node);
140562306a36Sopenharmony_ci		if (irq != pcswi->irq)
140662306a36Sopenharmony_ci			continue;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		soc_mask = pcs_soc->irq_enable_mask;
140962306a36Sopenharmony_ci		raw_spin_lock(&pcs->lock);
141062306a36Sopenharmony_ci		mask = pcs->read(pcswi->reg);
141162306a36Sopenharmony_ci		if (enable)
141262306a36Sopenharmony_ci			mask |= soc_mask;
141362306a36Sopenharmony_ci		else
141462306a36Sopenharmony_ci			mask &= ~soc_mask;
141562306a36Sopenharmony_ci		pcs->write(mask, pcswi->reg);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		/* flush posted write */
141862306a36Sopenharmony_ci		mask = pcs->read(pcswi->reg);
141962306a36Sopenharmony_ci		raw_spin_unlock(&pcs->lock);
142062306a36Sopenharmony_ci	}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	if (pcs_soc->rearm)
142362306a36Sopenharmony_ci		pcs_soc->rearm();
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci/**
142762306a36Sopenharmony_ci * pcs_irq_mask() - mask pinctrl interrupt
142862306a36Sopenharmony_ci * @d: interrupt data
142962306a36Sopenharmony_ci */
143062306a36Sopenharmony_cistatic void pcs_irq_mask(struct irq_data *d)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = irq_data_get_irq_chip_data(d);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	pcs_irq_set(pcs_soc, d->irq, false);
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci/**
143862306a36Sopenharmony_ci * pcs_irq_unmask() - unmask pinctrl interrupt
143962306a36Sopenharmony_ci * @d: interrupt data
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_cistatic void pcs_irq_unmask(struct irq_data *d)
144262306a36Sopenharmony_ci{
144362306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = irq_data_get_irq_chip_data(d);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	pcs_irq_set(pcs_soc, d->irq, true);
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci/**
144962306a36Sopenharmony_ci * pcs_irq_set_wake() - toggle the suspend and resume wake up
145062306a36Sopenharmony_ci * @d: interrupt data
145162306a36Sopenharmony_ci * @state: wake-up state
145262306a36Sopenharmony_ci *
145362306a36Sopenharmony_ci * Note that this should be called only for suspend and resume.
145462306a36Sopenharmony_ci * For runtime PM, the wake-up events should be enabled by default.
145562306a36Sopenharmony_ci */
145662306a36Sopenharmony_cistatic int pcs_irq_set_wake(struct irq_data *d, unsigned int state)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	if (state)
145962306a36Sopenharmony_ci		pcs_irq_unmask(d);
146062306a36Sopenharmony_ci	else
146162306a36Sopenharmony_ci		pcs_irq_mask(d);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	return 0;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci/**
146762306a36Sopenharmony_ci * pcs_irq_handle() - common interrupt handler
146862306a36Sopenharmony_ci * @pcs_soc: SoC specific settings
146962306a36Sopenharmony_ci *
147062306a36Sopenharmony_ci * Note that this currently assumes we have one interrupt bit per
147162306a36Sopenharmony_ci * mux register. This interrupt is typically used for wake-up events.
147262306a36Sopenharmony_ci * For more complex interrupts different handlers can be specified.
147362306a36Sopenharmony_ci */
147462306a36Sopenharmony_cistatic int pcs_irq_handle(struct pcs_soc_data *pcs_soc)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct pcs_device *pcs;
147762306a36Sopenharmony_ci	struct list_head *pos;
147862306a36Sopenharmony_ci	int count = 0;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	pcs = container_of(pcs_soc, struct pcs_device, socdata);
148162306a36Sopenharmony_ci	list_for_each(pos, &pcs->irqs) {
148262306a36Sopenharmony_ci		struct pcs_interrupt *pcswi;
148362306a36Sopenharmony_ci		unsigned mask;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci		pcswi = list_entry(pos, struct pcs_interrupt, node);
148662306a36Sopenharmony_ci		raw_spin_lock(&pcs->lock);
148762306a36Sopenharmony_ci		mask = pcs->read(pcswi->reg);
148862306a36Sopenharmony_ci		raw_spin_unlock(&pcs->lock);
148962306a36Sopenharmony_ci		if (mask & pcs_soc->irq_status_mask) {
149062306a36Sopenharmony_ci			generic_handle_domain_irq(pcs->domain,
149162306a36Sopenharmony_ci						  pcswi->hwirq);
149262306a36Sopenharmony_ci			count++;
149362306a36Sopenharmony_ci		}
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	return count;
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci/**
150062306a36Sopenharmony_ci * pcs_irq_handler() - handler for the shared interrupt case
150162306a36Sopenharmony_ci * @irq: interrupt
150262306a36Sopenharmony_ci * @d: data
150362306a36Sopenharmony_ci *
150462306a36Sopenharmony_ci * Use this for cases where multiple instances of
150562306a36Sopenharmony_ci * pinctrl-single share a single interrupt like on omaps.
150662306a36Sopenharmony_ci */
150762306a36Sopenharmony_cistatic irqreturn_t pcs_irq_handler(int irq, void *d)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = d;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	return pcs_irq_handle(pcs_soc) ? IRQ_HANDLED : IRQ_NONE;
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci/**
151562306a36Sopenharmony_ci * pcs_irq_chain_handler() - handler for the dedicated chained interrupt case
151662306a36Sopenharmony_ci * @desc: interrupt descriptor
151762306a36Sopenharmony_ci *
151862306a36Sopenharmony_ci * Use this if you have a separate interrupt for each
151962306a36Sopenharmony_ci * pinctrl-single instance.
152062306a36Sopenharmony_ci */
152162306a36Sopenharmony_cistatic void pcs_irq_chain_handler(struct irq_desc *desc)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = irq_desc_get_handler_data(desc);
152462306a36Sopenharmony_ci	struct irq_chip *chip;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	chip = irq_desc_get_chip(desc);
152762306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
152862306a36Sopenharmony_ci	pcs_irq_handle(pcs_soc);
152962306a36Sopenharmony_ci	/* REVISIT: export and add handle_bad_irq(irq, desc)? */
153062306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_cistatic int pcs_irqdomain_map(struct irq_domain *d, unsigned int irq,
153462306a36Sopenharmony_ci			     irq_hw_number_t hwirq)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = d->host_data;
153762306a36Sopenharmony_ci	struct pcs_device *pcs;
153862306a36Sopenharmony_ci	struct pcs_interrupt *pcswi;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	pcs = container_of(pcs_soc, struct pcs_device, socdata);
154162306a36Sopenharmony_ci	pcswi = devm_kzalloc(pcs->dev, sizeof(*pcswi), GFP_KERNEL);
154262306a36Sopenharmony_ci	if (!pcswi)
154362306a36Sopenharmony_ci		return -ENOMEM;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	pcswi->reg = pcs->base + hwirq;
154662306a36Sopenharmony_ci	pcswi->hwirq = hwirq;
154762306a36Sopenharmony_ci	pcswi->irq = irq;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	mutex_lock(&pcs->mutex);
155062306a36Sopenharmony_ci	list_add_tail(&pcswi->node, &pcs->irqs);
155162306a36Sopenharmony_ci	mutex_unlock(&pcs->mutex);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	irq_set_chip_data(irq, pcs_soc);
155462306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, &pcs->chip,
155562306a36Sopenharmony_ci				 handle_level_irq);
155662306a36Sopenharmony_ci	irq_set_lockdep_class(irq, &pcs_lock_class, &pcs_request_class);
155762306a36Sopenharmony_ci	irq_set_noprobe(irq);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	return 0;
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_cistatic const struct irq_domain_ops pcs_irqdomain_ops = {
156362306a36Sopenharmony_ci	.map = pcs_irqdomain_map,
156462306a36Sopenharmony_ci	.xlate = irq_domain_xlate_onecell,
156562306a36Sopenharmony_ci};
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci/**
156862306a36Sopenharmony_ci * pcs_irq_init_chained_handler() - set up a chained interrupt handler
156962306a36Sopenharmony_ci * @pcs: pcs driver instance
157062306a36Sopenharmony_ci * @np: device node pointer
157162306a36Sopenharmony_ci */
157262306a36Sopenharmony_cistatic int pcs_irq_init_chained_handler(struct pcs_device *pcs,
157362306a36Sopenharmony_ci					struct device_node *np)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct pcs_soc_data *pcs_soc = &pcs->socdata;
157662306a36Sopenharmony_ci	const char *name = "pinctrl";
157762306a36Sopenharmony_ci	int num_irqs;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	if (!pcs_soc->irq_enable_mask ||
158062306a36Sopenharmony_ci	    !pcs_soc->irq_status_mask) {
158162306a36Sopenharmony_ci		pcs_soc->irq = -1;
158262306a36Sopenharmony_ci		return -EINVAL;
158362306a36Sopenharmony_ci	}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	INIT_LIST_HEAD(&pcs->irqs);
158662306a36Sopenharmony_ci	pcs->chip.name = name;
158762306a36Sopenharmony_ci	pcs->chip.irq_ack = pcs_irq_mask;
158862306a36Sopenharmony_ci	pcs->chip.irq_mask = pcs_irq_mask;
158962306a36Sopenharmony_ci	pcs->chip.irq_unmask = pcs_irq_unmask;
159062306a36Sopenharmony_ci	pcs->chip.irq_set_wake = pcs_irq_set_wake;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	if (PCS_QUIRK_HAS_SHARED_IRQ) {
159362306a36Sopenharmony_ci		int res;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci		res = request_irq(pcs_soc->irq, pcs_irq_handler,
159662306a36Sopenharmony_ci				  IRQF_SHARED | IRQF_NO_SUSPEND |
159762306a36Sopenharmony_ci				  IRQF_NO_THREAD,
159862306a36Sopenharmony_ci				  name, pcs_soc);
159962306a36Sopenharmony_ci		if (res) {
160062306a36Sopenharmony_ci			pcs_soc->irq = -1;
160162306a36Sopenharmony_ci			return res;
160262306a36Sopenharmony_ci		}
160362306a36Sopenharmony_ci	} else {
160462306a36Sopenharmony_ci		irq_set_chained_handler_and_data(pcs_soc->irq,
160562306a36Sopenharmony_ci						 pcs_irq_chain_handler,
160662306a36Sopenharmony_ci						 pcs_soc);
160762306a36Sopenharmony_ci	}
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/*
161062306a36Sopenharmony_ci	 * We can use the register offset as the hardirq
161162306a36Sopenharmony_ci	 * number as irq_domain_add_simple maps them lazily.
161262306a36Sopenharmony_ci	 * This way we can easily support more than one
161362306a36Sopenharmony_ci	 * interrupt per function if needed.
161462306a36Sopenharmony_ci	 */
161562306a36Sopenharmony_ci	num_irqs = pcs->size;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	pcs->domain = irq_domain_add_simple(np, num_irqs, 0,
161862306a36Sopenharmony_ci					    &pcs_irqdomain_ops,
161962306a36Sopenharmony_ci					    pcs_soc);
162062306a36Sopenharmony_ci	if (!pcs->domain) {
162162306a36Sopenharmony_ci		irq_set_chained_handler(pcs_soc->irq, NULL);
162262306a36Sopenharmony_ci		return -EINVAL;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	return 0;
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci#ifdef CONFIG_PM
162962306a36Sopenharmony_cistatic int pcs_save_context(struct pcs_device *pcs)
163062306a36Sopenharmony_ci{
163162306a36Sopenharmony_ci	int i, mux_bytes;
163262306a36Sopenharmony_ci	u64 *regsl;
163362306a36Sopenharmony_ci	u32 *regsw;
163462306a36Sopenharmony_ci	u16 *regshw;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	mux_bytes = pcs->width / BITS_PER_BYTE;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	if (!pcs->saved_vals) {
163962306a36Sopenharmony_ci		pcs->saved_vals = devm_kzalloc(pcs->dev, pcs->size, GFP_ATOMIC);
164062306a36Sopenharmony_ci		if (!pcs->saved_vals)
164162306a36Sopenharmony_ci			return -ENOMEM;
164262306a36Sopenharmony_ci	}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	switch (pcs->width) {
164562306a36Sopenharmony_ci	case 64:
164662306a36Sopenharmony_ci		regsl = pcs->saved_vals;
164762306a36Sopenharmony_ci		for (i = 0; i < pcs->size; i += mux_bytes)
164862306a36Sopenharmony_ci			*regsl++ = pcs->read(pcs->base + i);
164962306a36Sopenharmony_ci		break;
165062306a36Sopenharmony_ci	case 32:
165162306a36Sopenharmony_ci		regsw = pcs->saved_vals;
165262306a36Sopenharmony_ci		for (i = 0; i < pcs->size; i += mux_bytes)
165362306a36Sopenharmony_ci			*regsw++ = pcs->read(pcs->base + i);
165462306a36Sopenharmony_ci		break;
165562306a36Sopenharmony_ci	case 16:
165662306a36Sopenharmony_ci		regshw = pcs->saved_vals;
165762306a36Sopenharmony_ci		for (i = 0; i < pcs->size; i += mux_bytes)
165862306a36Sopenharmony_ci			*regshw++ = pcs->read(pcs->base + i);
165962306a36Sopenharmony_ci		break;
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	return 0;
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cistatic void pcs_restore_context(struct pcs_device *pcs)
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci	int i, mux_bytes;
166862306a36Sopenharmony_ci	u64 *regsl;
166962306a36Sopenharmony_ci	u32 *regsw;
167062306a36Sopenharmony_ci	u16 *regshw;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	mux_bytes = pcs->width / BITS_PER_BYTE;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	switch (pcs->width) {
167562306a36Sopenharmony_ci	case 64:
167662306a36Sopenharmony_ci		regsl = pcs->saved_vals;
167762306a36Sopenharmony_ci		for (i = 0; i < pcs->size; i += mux_bytes)
167862306a36Sopenharmony_ci			pcs->write(*regsl++, pcs->base + i);
167962306a36Sopenharmony_ci		break;
168062306a36Sopenharmony_ci	case 32:
168162306a36Sopenharmony_ci		regsw = pcs->saved_vals;
168262306a36Sopenharmony_ci		for (i = 0; i < pcs->size; i += mux_bytes)
168362306a36Sopenharmony_ci			pcs->write(*regsw++, pcs->base + i);
168462306a36Sopenharmony_ci		break;
168562306a36Sopenharmony_ci	case 16:
168662306a36Sopenharmony_ci		regshw = pcs->saved_vals;
168762306a36Sopenharmony_ci		for (i = 0; i < pcs->size; i += mux_bytes)
168862306a36Sopenharmony_ci			pcs->write(*regshw++, pcs->base + i);
168962306a36Sopenharmony_ci		break;
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_cistatic int pinctrl_single_suspend(struct platform_device *pdev,
169462306a36Sopenharmony_ci					pm_message_t state)
169562306a36Sopenharmony_ci{
169662306a36Sopenharmony_ci	struct pcs_device *pcs;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	pcs = platform_get_drvdata(pdev);
169962306a36Sopenharmony_ci	if (!pcs)
170062306a36Sopenharmony_ci		return -EINVAL;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	if (pcs->flags & PCS_CONTEXT_LOSS_OFF) {
170362306a36Sopenharmony_ci		int ret;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci		ret = pcs_save_context(pcs);
170662306a36Sopenharmony_ci		if (ret < 0)
170762306a36Sopenharmony_ci			return ret;
170862306a36Sopenharmony_ci	}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	return pinctrl_force_sleep(pcs->pctl);
171162306a36Sopenharmony_ci}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_cistatic int pinctrl_single_resume(struct platform_device *pdev)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	struct pcs_device *pcs;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	pcs = platform_get_drvdata(pdev);
171862306a36Sopenharmony_ci	if (!pcs)
171962306a36Sopenharmony_ci		return -EINVAL;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (pcs->flags & PCS_CONTEXT_LOSS_OFF)
172262306a36Sopenharmony_ci		pcs_restore_context(pcs);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	return pinctrl_force_default(pcs->pctl);
172562306a36Sopenharmony_ci}
172662306a36Sopenharmony_ci#endif
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci/**
172962306a36Sopenharmony_ci * pcs_quirk_missing_pinctrl_cells - handle legacy binding
173062306a36Sopenharmony_ci * @pcs: pinctrl driver instance
173162306a36Sopenharmony_ci * @np: device tree node
173262306a36Sopenharmony_ci * @cells: number of cells
173362306a36Sopenharmony_ci *
173462306a36Sopenharmony_ci * Handle legacy binding with no #pinctrl-cells. This should be
173562306a36Sopenharmony_ci * always two pinctrl-single,bit-per-mux and one for others.
173662306a36Sopenharmony_ci * At some point we may want to consider removing this.
173762306a36Sopenharmony_ci */
173862306a36Sopenharmony_cistatic int pcs_quirk_missing_pinctrl_cells(struct pcs_device *pcs,
173962306a36Sopenharmony_ci					   struct device_node *np,
174062306a36Sopenharmony_ci					   int cells)
174162306a36Sopenharmony_ci{
174262306a36Sopenharmony_ci	struct property *p;
174362306a36Sopenharmony_ci	const char *name = "#pinctrl-cells";
174462306a36Sopenharmony_ci	int error;
174562306a36Sopenharmony_ci	u32 val;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	error = of_property_read_u32(np, name, &val);
174862306a36Sopenharmony_ci	if (!error)
174962306a36Sopenharmony_ci		return 0;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	dev_warn(pcs->dev, "please update dts to use %s = <%i>\n",
175262306a36Sopenharmony_ci		 name, cells);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	p = devm_kzalloc(pcs->dev, sizeof(*p), GFP_KERNEL);
175562306a36Sopenharmony_ci	if (!p)
175662306a36Sopenharmony_ci		return -ENOMEM;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	p->length = sizeof(__be32);
175962306a36Sopenharmony_ci	p->value = devm_kzalloc(pcs->dev, sizeof(__be32), GFP_KERNEL);
176062306a36Sopenharmony_ci	if (!p->value)
176162306a36Sopenharmony_ci		return -ENOMEM;
176262306a36Sopenharmony_ci	*(__be32 *)p->value = cpu_to_be32(cells);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	p->name = devm_kstrdup(pcs->dev, name, GFP_KERNEL);
176562306a36Sopenharmony_ci	if (!p->name)
176662306a36Sopenharmony_ci		return -ENOMEM;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	pcs->missing_nr_pinctrl_cells = p;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_PINCTRL_SINGLE)
177162306a36Sopenharmony_ci	error = of_add_property(np, pcs->missing_nr_pinctrl_cells);
177262306a36Sopenharmony_ci#endif
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	return error;
177562306a36Sopenharmony_ci}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_cistatic int pcs_probe(struct platform_device *pdev)
177862306a36Sopenharmony_ci{
177962306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
178062306a36Sopenharmony_ci	struct pcs_pdata *pdata;
178162306a36Sopenharmony_ci	struct resource *res;
178262306a36Sopenharmony_ci	struct pcs_device *pcs;
178362306a36Sopenharmony_ci	const struct pcs_soc_data *soc;
178462306a36Sopenharmony_ci	int ret;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	soc = of_device_get_match_data(&pdev->dev);
178762306a36Sopenharmony_ci	if (WARN_ON(!soc))
178862306a36Sopenharmony_ci		return -EINVAL;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	pcs = devm_kzalloc(&pdev->dev, sizeof(*pcs), GFP_KERNEL);
179162306a36Sopenharmony_ci	if (!pcs)
179262306a36Sopenharmony_ci		return -ENOMEM;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	pcs->dev = &pdev->dev;
179562306a36Sopenharmony_ci	pcs->np = np;
179662306a36Sopenharmony_ci	raw_spin_lock_init(&pcs->lock);
179762306a36Sopenharmony_ci	mutex_init(&pcs->mutex);
179862306a36Sopenharmony_ci	INIT_LIST_HEAD(&pcs->gpiofuncs);
179962306a36Sopenharmony_ci	pcs->flags = soc->flags;
180062306a36Sopenharmony_ci	memcpy(&pcs->socdata, soc, sizeof(*soc));
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	ret = of_property_read_u32(np, "pinctrl-single,register-width",
180362306a36Sopenharmony_ci				   &pcs->width);
180462306a36Sopenharmony_ci	if (ret) {
180562306a36Sopenharmony_ci		dev_err(pcs->dev, "register width not specified\n");
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci		return ret;
180862306a36Sopenharmony_ci	}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	ret = of_property_read_u32(np, "pinctrl-single,function-mask",
181162306a36Sopenharmony_ci				   &pcs->fmask);
181262306a36Sopenharmony_ci	if (!ret) {
181362306a36Sopenharmony_ci		pcs->fshift = __ffs(pcs->fmask);
181462306a36Sopenharmony_ci		pcs->fmax = pcs->fmask >> pcs->fshift;
181562306a36Sopenharmony_ci	} else {
181662306a36Sopenharmony_ci		/* If mask property doesn't exist, function mux is invalid. */
181762306a36Sopenharmony_ci		pcs->fmask = 0;
181862306a36Sopenharmony_ci		pcs->fshift = 0;
181962306a36Sopenharmony_ci		pcs->fmax = 0;
182062306a36Sopenharmony_ci	}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	ret = of_property_read_u32(np, "pinctrl-single,function-off",
182362306a36Sopenharmony_ci					&pcs->foff);
182462306a36Sopenharmony_ci	if (ret)
182562306a36Sopenharmony_ci		pcs->foff = PCS_OFF_DISABLED;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	pcs->bits_per_mux = of_property_read_bool(np,
182862306a36Sopenharmony_ci						  "pinctrl-single,bit-per-mux");
182962306a36Sopenharmony_ci	ret = pcs_quirk_missing_pinctrl_cells(pcs, np,
183062306a36Sopenharmony_ci					      pcs->bits_per_mux ? 2 : 1);
183162306a36Sopenharmony_ci	if (ret) {
183262306a36Sopenharmony_ci		dev_err(&pdev->dev, "unable to patch #pinctrl-cells\n");
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci		return ret;
183562306a36Sopenharmony_ci	}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
183862306a36Sopenharmony_ci	if (!res) {
183962306a36Sopenharmony_ci		dev_err(pcs->dev, "could not get resource\n");
184062306a36Sopenharmony_ci		return -ENODEV;
184162306a36Sopenharmony_ci	}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	pcs->res = devm_request_mem_region(pcs->dev, res->start,
184462306a36Sopenharmony_ci			resource_size(res), DRIVER_NAME);
184562306a36Sopenharmony_ci	if (!pcs->res) {
184662306a36Sopenharmony_ci		dev_err(pcs->dev, "could not get mem_region\n");
184762306a36Sopenharmony_ci		return -EBUSY;
184862306a36Sopenharmony_ci	}
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	pcs->size = resource_size(pcs->res);
185162306a36Sopenharmony_ci	pcs->base = devm_ioremap(pcs->dev, pcs->res->start, pcs->size);
185262306a36Sopenharmony_ci	if (!pcs->base) {
185362306a36Sopenharmony_ci		dev_err(pcs->dev, "could not ioremap\n");
185462306a36Sopenharmony_ci		return -ENODEV;
185562306a36Sopenharmony_ci	}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	platform_set_drvdata(pdev, pcs);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	switch (pcs->width) {
186062306a36Sopenharmony_ci	case 8:
186162306a36Sopenharmony_ci		pcs->read = pcs_readb;
186262306a36Sopenharmony_ci		pcs->write = pcs_writeb;
186362306a36Sopenharmony_ci		break;
186462306a36Sopenharmony_ci	case 16:
186562306a36Sopenharmony_ci		pcs->read = pcs_readw;
186662306a36Sopenharmony_ci		pcs->write = pcs_writew;
186762306a36Sopenharmony_ci		break;
186862306a36Sopenharmony_ci	case 32:
186962306a36Sopenharmony_ci		pcs->read = pcs_readl;
187062306a36Sopenharmony_ci		pcs->write = pcs_writel;
187162306a36Sopenharmony_ci		break;
187262306a36Sopenharmony_ci	default:
187362306a36Sopenharmony_ci		break;
187462306a36Sopenharmony_ci	}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	pcs->desc.name = DRIVER_NAME;
187762306a36Sopenharmony_ci	pcs->desc.pctlops = &pcs_pinctrl_ops;
187862306a36Sopenharmony_ci	pcs->desc.pmxops = &pcs_pinmux_ops;
187962306a36Sopenharmony_ci	if (PCS_HAS_PINCONF)
188062306a36Sopenharmony_ci		pcs->desc.confops = &pcs_pinconf_ops;
188162306a36Sopenharmony_ci	pcs->desc.owner = THIS_MODULE;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	ret = pcs_allocate_pin_table(pcs);
188462306a36Sopenharmony_ci	if (ret < 0)
188562306a36Sopenharmony_ci		goto free;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	ret = pinctrl_register_and_init(&pcs->desc, pcs->dev, pcs, &pcs->pctl);
188862306a36Sopenharmony_ci	if (ret) {
188962306a36Sopenharmony_ci		dev_err(pcs->dev, "could not register single pinctrl driver\n");
189062306a36Sopenharmony_ci		goto free;
189162306a36Sopenharmony_ci	}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	ret = pcs_add_gpio_func(np, pcs);
189462306a36Sopenharmony_ci	if (ret < 0)
189562306a36Sopenharmony_ci		goto free;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	pcs->socdata.irq = irq_of_parse_and_map(np, 0);
189862306a36Sopenharmony_ci	if (pcs->socdata.irq)
189962306a36Sopenharmony_ci		pcs->flags |= PCS_FEAT_IRQ;
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	/* We still need auxdata for some omaps for PRM interrupts */
190262306a36Sopenharmony_ci	pdata = dev_get_platdata(&pdev->dev);
190362306a36Sopenharmony_ci	if (pdata) {
190462306a36Sopenharmony_ci		if (pdata->rearm)
190562306a36Sopenharmony_ci			pcs->socdata.rearm = pdata->rearm;
190662306a36Sopenharmony_ci		if (pdata->irq) {
190762306a36Sopenharmony_ci			pcs->socdata.irq = pdata->irq;
190862306a36Sopenharmony_ci			pcs->flags |= PCS_FEAT_IRQ;
190962306a36Sopenharmony_ci		}
191062306a36Sopenharmony_ci	}
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	if (PCS_HAS_IRQ) {
191362306a36Sopenharmony_ci		ret = pcs_irq_init_chained_handler(pcs, np);
191462306a36Sopenharmony_ci		if (ret < 0)
191562306a36Sopenharmony_ci			dev_warn(pcs->dev, "initialized with no interrupts\n");
191662306a36Sopenharmony_ci	}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	dev_info(pcs->dev, "%i pins, size %u\n", pcs->desc.npins, pcs->size);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	return pinctrl_enable(pcs->pctl);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cifree:
192362306a36Sopenharmony_ci	pcs_free_resources(pcs);
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	return ret;
192662306a36Sopenharmony_ci}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_cistatic int pcs_remove(struct platform_device *pdev)
192962306a36Sopenharmony_ci{
193062306a36Sopenharmony_ci	struct pcs_device *pcs = platform_get_drvdata(pdev);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	if (!pcs)
193362306a36Sopenharmony_ci		return 0;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	pcs_free_resources(pcs);
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	return 0;
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_cistatic const struct pcs_soc_data pinctrl_single_omap_wkup = {
194162306a36Sopenharmony_ci	.flags = PCS_QUIRK_SHARED_IRQ,
194262306a36Sopenharmony_ci	.irq_enable_mask = (1 << 14),	/* OMAP_WAKEUP_EN */
194362306a36Sopenharmony_ci	.irq_status_mask = (1 << 15),	/* OMAP_WAKEUP_EVENT */
194462306a36Sopenharmony_ci};
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_cistatic const struct pcs_soc_data pinctrl_single_dra7 = {
194762306a36Sopenharmony_ci	.irq_enable_mask = (1 << 24),	/* WAKEUPENABLE */
194862306a36Sopenharmony_ci	.irq_status_mask = (1 << 25),	/* WAKEUPEVENT */
194962306a36Sopenharmony_ci};
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_cistatic const struct pcs_soc_data pinctrl_single_am437x = {
195262306a36Sopenharmony_ci	.flags = PCS_QUIRK_SHARED_IRQ | PCS_CONTEXT_LOSS_OFF,
195362306a36Sopenharmony_ci	.irq_enable_mask = (1 << 29),   /* OMAP_WAKEUP_EN */
195462306a36Sopenharmony_ci	.irq_status_mask = (1 << 30),   /* OMAP_WAKEUP_EVENT */
195562306a36Sopenharmony_ci};
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_cistatic const struct pcs_soc_data pinctrl_single_am654 = {
195862306a36Sopenharmony_ci	.flags = PCS_QUIRK_SHARED_IRQ | PCS_CONTEXT_LOSS_OFF,
195962306a36Sopenharmony_ci	.irq_enable_mask = (1 << 29),   /* WKUP_EN */
196062306a36Sopenharmony_ci	.irq_status_mask = (1 << 30),   /* WKUP_EVT */
196162306a36Sopenharmony_ci};
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_cistatic const struct pcs_soc_data pinctrl_single = {
196462306a36Sopenharmony_ci};
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic const struct pcs_soc_data pinconf_single = {
196762306a36Sopenharmony_ci	.flags = PCS_FEAT_PINCONF,
196862306a36Sopenharmony_ci};
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_cistatic const struct of_device_id pcs_of_match[] = {
197162306a36Sopenharmony_ci	{ .compatible = "ti,am437-padconf", .data = &pinctrl_single_am437x },
197262306a36Sopenharmony_ci	{ .compatible = "ti,am654-padconf", .data = &pinctrl_single_am654 },
197362306a36Sopenharmony_ci	{ .compatible = "ti,dra7-padconf", .data = &pinctrl_single_dra7 },
197462306a36Sopenharmony_ci	{ .compatible = "ti,omap3-padconf", .data = &pinctrl_single_omap_wkup },
197562306a36Sopenharmony_ci	{ .compatible = "ti,omap4-padconf", .data = &pinctrl_single_omap_wkup },
197662306a36Sopenharmony_ci	{ .compatible = "ti,omap5-padconf", .data = &pinctrl_single_omap_wkup },
197762306a36Sopenharmony_ci	{ .compatible = "pinctrl-single", .data = &pinctrl_single },
197862306a36Sopenharmony_ci	{ .compatible = "pinconf-single", .data = &pinconf_single },
197962306a36Sopenharmony_ci	{ },
198062306a36Sopenharmony_ci};
198162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pcs_of_match);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_cistatic struct platform_driver pcs_driver = {
198462306a36Sopenharmony_ci	.probe		= pcs_probe,
198562306a36Sopenharmony_ci	.remove		= pcs_remove,
198662306a36Sopenharmony_ci	.driver = {
198762306a36Sopenharmony_ci		.name		= DRIVER_NAME,
198862306a36Sopenharmony_ci		.of_match_table	= pcs_of_match,
198962306a36Sopenharmony_ci	},
199062306a36Sopenharmony_ci#ifdef CONFIG_PM
199162306a36Sopenharmony_ci	.suspend = pinctrl_single_suspend,
199262306a36Sopenharmony_ci	.resume = pinctrl_single_resume,
199362306a36Sopenharmony_ci#endif
199462306a36Sopenharmony_ci};
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_cimodule_platform_driver(pcs_driver);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ciMODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
199962306a36Sopenharmony_ciMODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
200062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2001