Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0529f386 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "pinctrl: qcom: add pinctrl driver for SLPI"

parents b6a8b068 5bea378b
Loading
Loading
Loading
Loading
+123 −0
Original line number Diff line number Diff line
Qualcomm Technologies, Inc. SLPI Pin controller driver

This DT bindings describes the Pin controller driver
being added for supporting SLPI (Sensor Low Power Island) TLMM
from QTI chipsets.

Following properties are for SLPI Pin controller device main node.
- compatible:
	Usage: required
	Value type: <string>
	Definition: must be "qcom,slpi-pinctrl"

- reg:
	Usage: required
	Value type: <prop-encoded-array>
	Definition: Base address and size of the SLPI TLMM register space.

- qcom,num-pins:
	Usage: required
	Value type: <u32>
	Definition: Number of PINs supported by the SLPI TLMM.

Please refer to pinctrl-bindings.txt in this directory for details of the
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".

The pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin or a list of pins. This configuration can include the
mux function to select on those pin(s), and various pin configuration
parameters, as listed below.

SUBNODES:

The name of each subnode is not important; all subnodes should be enumerated
and processed purely based on their content.

Each subnode only affects those parameters that are explicitly listed. In
other words, a subnode that lists a mux function but no pin configuration
parameters implies no information about any pin configuration parameters.
Similarly, a pin subnode that describes a pullup parameter implies no
information about e.g. the mux function.

The following generic properties as defined in pinctrl-bindings.txt are valid
to specify in a pin configuration subnode:

- pins:
	Usage: required
	Value type: <string-array>
	Definition: List of gpio pins affected by the properties specified in
		    this subnode.  Valid pins are: gpio0-gpio31 for LPI.

- function:
	Usage: required
	Value type: <string>
	Definition: Specify the alternative function to be configured for the
		    specified pins. Valid values are:
			"gpio",
			"func1",
			"func2",
			"func3",
			"func4",
			"func5"

- bias-disable:
	Usage: optional
	Value type: <none>
	Definition: The specified pins should be configured as no pull.

- bias-pull-down:
	Usage: optional
	Value type: <none>
	Definition: The specified pins should be configured as pull down.

- bias-bus-hold:
	Usage: optional
	Value type: <none>
	Definition: The specified pins should be configured as bus-keeper mode.

- bias-pull-up:
	Usage: optional
	Value type: <empty>
	Definition: The specified pins should be configured as pull up.

- qcom,drive-strength:
	Usage: optional
	Value type: <u32>
	Definition: Selects the drive strength for the specified pins.

Example:

	slpi_tlmm: slpi_pinctrl@02b40000 {
		compatible = "qcom,slpi-pinctrl";
		qcom,num-pins = <14>;
		reg = <0x02b40000 0x20000>;

		slpi_mclk0_active: slpi_mclk0_active {
			mux {
				pins = "gpio0", "gpio1", "gpio2", "gpio3";
				function = "func2";
			};

			config {
				pins = "gpio0", "gpio1", "gpio2", "gpio3";
				drive-strength = <8>;
				bias-disable;
			};
		};

		slpi_mclk0_sleep: slpi_mclk0_sleep {
			mux {
				pins = "gpio0", "gpio1", "gpio2", "gpio3";
				function = "func2";
			};

			config {
				pins = "gpio0", "gpio1", "gpio2", "gpio3";
				drive-strength = <2>;
				bias-pull-down;
			};
		};

	};
+7 −0
Original line number Diff line number Diff line
@@ -198,4 +198,11 @@ config PINCTRL_SDXPRAIRIE
	  the Qualcomm Technologies Inc TLMM block found on the Qualcomm
	  Technologies Inc SDXPRAIRIE platform.

config PINCTRL_SLPI
	tristate "Qualcomm Technologies, Inc SLPI pin controller driver"
	depends on GPIOLIB && OF
	help
	  This is the pinctrl, pinmux and pinconf driver for the
	  SLPI pin controller block.

endif
+1 −0
Original line number Diff line number Diff line
@@ -24,3 +24,4 @@ obj-$(CONFIG_PINCTRL_SDMSHRIKE) += pinctrl-sdmshrike.o
obj-$(CONFIG_PINCTRL_SM6150) += pinctrl-sm6150.o
obj-$(CONFIG_PINCTRL_SDXPRAIRIE) += pinctrl-sdxprairie.o
obj-$(CONFIG_PINCTRL_SDMMAGPIE) += pinctrl-sdmmagpie.o
obj-$(CONFIG_PINCTRL_SLPI) 	+= pinctrl-slpi.o
+461 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>

#include "../core.h"
#include "../pinctrl-utils.h"

/**
 * struct slpi_pin - SLPI pin definition
 * @name:	Name of the pin.
 * @ctl_reg:	Offset of the register holding control bits for this group.
 * @io_reg:	Offset of the register holding input/output bits for this group.
 * @pull_bit:	Offset in @ctl_reg for the bias configuration.
 * @mux_bit:	Offset in @ctl_reg for the pinmux function selection.
 * @drv_bit:	Offset in @ctl_reg for the drive strength configuration.
 * @oe_bit:	Offset in @ctl_reg for controlling output enable.
 * @in_bit:	Offset in @io_reg for the input bit value.
 * @out_bit:	Offset in @io_reg for the output bit value.
 */
struct slpi_pin {
	void __iomem *base;
	unsigned int offset;

	unsigned int ctl_reg;
	unsigned int io_reg;

	unsigned int pull_bit:5;
	unsigned int mux_bit:5;
	unsigned int drv_bit:5;
	unsigned int oe_bit:5;
	unsigned int in_bit:5;
	unsigned int out_bit:5;
};


/* The index of each function in slpi_pin_functions[] array */
enum slpi_pin_func_index {
	SLPI_PIN_FUNC_INDEX_GPIO	= 0x00,
	SLPI_PIN_FUNC_INDEX_FUNC1	= 0x01,
	SLPI_PIN_FUNC_INDEX_FUNC2	= 0x02,
	SLPI_PIN_FUNC_INDEX_FUNC3	= 0x03,
	SLPI_PIN_FUNC_INDEX_FUNC4	= 0x04,
	SLPI_PIN_FUNC_INDEX_FUNC5	= 0x05,
};

#define SLPI_PIN_FUNC_GPIO	"gpio"
#define SLPI_PIN_FUNC_FUNC1	"func1"
#define SLPI_PIN_FUNC_FUNC2	"func2"
#define SLPI_PIN_FUNC_FUNC3	"func3"
#define SLPI_PIN_FUNC_FUNC4	"func4"
#define SLPI_PIN_FUNC_FUNC5	"func5"

static const char *const slpi_gpio_groups[] = {
	"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
	"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13",
};

static const char *const slpi_pin_functions[] = {
	[SLPI_PIN_FUNC_INDEX_GPIO]	= SLPI_PIN_FUNC_GPIO,
	[SLPI_PIN_FUNC_INDEX_FUNC1]	= SLPI_PIN_FUNC_FUNC1,
	[SLPI_PIN_FUNC_INDEX_FUNC2]	= SLPI_PIN_FUNC_FUNC2,
	[SLPI_PIN_FUNC_INDEX_FUNC3]	= SLPI_PIN_FUNC_FUNC3,
	[SLPI_PIN_FUNC_INDEX_FUNC4]	= SLPI_PIN_FUNC_FUNC4,
	[SLPI_PIN_FUNC_INDEX_FUNC5]	= SLPI_PIN_FUNC_FUNC5,
};

static unsigned int slpi_read(struct slpi_pin *pin, u32 reg)
{
	return readl_relaxed(pin->base + pin->offset + reg);
}

static void  slpi_write(u32 val, struct slpi_pin *pin,  u32 reg)
{
	return writel_relaxed(val, pin->base + pin->offset + reg);
}

static int slpi_get_groups_count(struct pinctrl_dev *pctldev)
{
	/* Every PIN is a group */
	return pctldev->desc->npins;
}

static const char *slpi_get_group_name(struct pinctrl_dev *pctldev,
					unsigned int pin)
{
	return pctldev->desc->pins[pin].name;
}

static int slpi_get_group_pins(struct pinctrl_dev *pctldev,
			      unsigned int pin,
			      const unsigned int **pins,
			      unsigned int *num_pins)
{
	*pins = &pctldev->desc->pins[pin].number;
	*num_pins = 1;
	return 0;
}

static const struct pinctrl_ops slpi_pinctrl_ops = {
	.get_groups_count	= slpi_get_groups_count,
	.get_group_name		= slpi_get_group_name,
	.get_group_pins		= slpi_get_group_pins,
	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
	.dt_free_map		= pinctrl_utils_free_map,
};

static int slpi_get_functions_count(struct pinctrl_dev *pctldev)
{
	return ARRAY_SIZE(slpi_pin_functions);
}

static const char *slpi_get_function_name(struct pinctrl_dev *pctldev,
					 unsigned int function)
{
	return slpi_pin_functions[function];
}

static int slpi_get_function_groups(struct pinctrl_dev *pctldev,
				   unsigned int function,
				   const char * const **groups,
				   unsigned int * const num_groups)
{
	*groups = slpi_gpio_groups;
	*num_groups = pctldev->desc->npins;
	return 0;
}

static int slpi_pinmux_set_mux(struct pinctrl_dev *pctldev,
			      unsigned int function,
			      unsigned int pin_index)
{
	struct slpi_pin *pin;
	u32 val;

	pin = pctldev->desc->pins[pin_index].drv_data;

	if (WARN_ON(function >= ARRAY_SIZE(slpi_pin_functions)))
		return -EINVAL;

	val = slpi_read(pin, pin->ctl_reg);
	val &= ~(0x7 << pin->mux_bit);
	val |= function << pin->mux_bit;
	slpi_write(val, pin, pin->ctl_reg);

	return 0;
}

static const struct pinmux_ops slpi_pinmux_ops = {
	.get_functions_count	= slpi_get_functions_count,
	.get_function_name	= slpi_get_function_name,
	.get_function_groups	= slpi_get_function_groups,
	.set_mux		= slpi_pinmux_set_mux,
};

static int slpi_config_reg(const struct slpi_pin *pin,
			  unsigned int param,
			  unsigned int *mask,
			  unsigned int *bit)
{
	switch (param) {
	case PIN_CONFIG_BIAS_DISABLE:
	case PIN_CONFIG_BIAS_PULL_DOWN:
	case PIN_CONFIG_BIAS_BUS_HOLD:
	case PIN_CONFIG_BIAS_PULL_UP:
		*bit = pin->pull_bit;
		*mask = 3;
		break;
	case PIN_CONFIG_DRIVE_STRENGTH:
		*bit = pin->drv_bit;
		*mask = 7;
		break;
	case PIN_CONFIG_OUTPUT:
	case PIN_CONFIG_INPUT_ENABLE:
		*bit = pin->oe_bit;
		*mask = 1;
		break;
	default:
		return -ENOTSUPP;
	}

	return 0;
}

#define MSM_NO_PULL	0
#define MSM_PULL_DOWN	1
#define MSM_KEEPER	2
#define MSM_PULL_UP	3

static unsigned int slpi_regval_to_drive(u32 val)
{
	return (val + 1) * 2;
}

static int slpi_config_group_get(struct pinctrl_dev *pctldev,
				unsigned int pin_index,
				unsigned long *config)
{
	unsigned int param = pinconf_to_config_param(*config);
	struct slpi_pin *pin;
	unsigned int mask;
	unsigned int arg;
	unsigned int bit;
	int ret;
	u32 val;

	pin = pctldev->desc->pins[pin_index].drv_data;
	ret = slpi_config_reg(pin, param, &mask, &bit);
	if (ret < 0)
		return ret;

	val = slpi_read(pin, pin->ctl_reg);
	arg = (val >> bit) & mask;

	/* Convert register value to pinconf value */
	switch (param) {
	case PIN_CONFIG_BIAS_DISABLE:
		arg = arg == MSM_NO_PULL;
		break;
	case PIN_CONFIG_BIAS_PULL_DOWN:
		arg = arg == MSM_PULL_DOWN;
		break;
	case PIN_CONFIG_BIAS_BUS_HOLD:
		arg = arg == MSM_KEEPER;
		break;
	case PIN_CONFIG_BIAS_PULL_UP:
		arg = arg == MSM_PULL_UP;
		break;
	case PIN_CONFIG_DRIVE_STRENGTH:
		arg = slpi_regval_to_drive(arg);
		break;
	case PIN_CONFIG_OUTPUT:
		/* Pin is not output */
		if (!arg)
			return -EINVAL;

		val = slpi_read(pin, pin->io_reg);
		arg = !!(val & BIT(pin->in_bit));
		break;
	case PIN_CONFIG_INPUT_ENABLE:
		/* Pin is output */
		if (arg)
			return -EINVAL;
		arg = 1;
		break;
	default:
		return -ENOTSUPP;
	}

	*config = pinconf_to_config_packed(param, arg);

	return 0;
}

static int slpi_config_group_set(struct pinctrl_dev *pctldev,
				unsigned int pin_index,
				unsigned long *configs,
				unsigned int num_configs)
{
	struct slpi_pin *pin;
	unsigned int param;
	unsigned int mask;
	unsigned int arg;
	unsigned int bit;
	int ret;
	u32 val;
	int i;

	pin = pctldev->desc->pins[pin_index].drv_data;

	for (i = 0; i < num_configs; i++) {
		param = pinconf_to_config_param(configs[i]);
		arg = pinconf_to_config_argument(configs[i]);

		ret = slpi_config_reg(pin, param, &mask, &bit);
		if (ret < 0)
			return ret;

		/* Convert pinconf values to register values */
		switch (param) {
		case PIN_CONFIG_BIAS_DISABLE:
			arg = MSM_NO_PULL;
			break;
		case PIN_CONFIG_BIAS_PULL_DOWN:
			arg = MSM_PULL_DOWN;
			break;
		case PIN_CONFIG_BIAS_BUS_HOLD:
			arg = MSM_KEEPER;
			break;
		case PIN_CONFIG_BIAS_PULL_UP:
			arg = MSM_PULL_UP;
			break;
		case PIN_CONFIG_DRIVE_STRENGTH:
			/* Check for invalid values */
			if (arg > 16 || arg < 2 || (arg % 2) != 0)
				arg = -1;
			else
				arg = (arg / 2) - 1;
			break;
		case PIN_CONFIG_OUTPUT:
			/* set output value */
			val = slpi_read(pin, pin->io_reg);
			if (arg)
				val |= BIT(pin->out_bit);
			else
				val &= ~BIT(pin->out_bit);
			slpi_write(val, pin, pin->io_reg);

			/* enable output */
			arg = 1;
			break;
		case PIN_CONFIG_INPUT_ENABLE:
			/* disable output */
			arg = 0;
			break;
		default:
			dev_err(pctldev->dev, "Unsupported config parameter: %x\n",
				param);
			return -EINVAL;
		}

		/* Range-check user-supplied value */
		if (arg & ~mask) {
			dev_err(pctldev->dev, "config %x: %x is invalid\n",
								param, arg);
			return -EINVAL;
		}

		val = slpi_read(pin, pin->ctl_reg);
		val &= ~(mask << bit);
		val |= arg << bit;
		slpi_write(val, pin, pin->ctl_reg);
	}

	return 0;
}

static const struct pinconf_ops slpi_pinconf_ops = {
	.is_generic		= true,
	.pin_config_group_get	= slpi_config_group_get,
	.pin_config_group_set	= slpi_config_group_set,
};

static int slpi_pinctrl_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct pinctrl_dev *pctldev;
	struct pinctrl_pin_desc *pindesc;
	struct pinctrl_desc *pctrldesc;
	struct slpi_pin *pin, *pins;
	struct resource *res;
	int ret, npins, i;
	void __iomem *base;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(base))
		return PTR_ERR(base);

	ret = of_property_read_u32(dev->of_node, "qcom,num-pins", &npins);
	if (ret < 0)
		return ret;

	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
	if (!pindesc)
		return -ENOMEM;

	WARN_ON(npins > ARRAY_SIZE(slpi_gpio_groups));

	pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
	if (!pins)
		return -ENOMEM;

	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
	if (!pctrldesc)
		return -ENOMEM;

	pctrldesc->pctlops = &slpi_pinctrl_ops;
	pctrldesc->pmxops = &slpi_pinmux_ops;
	pctrldesc->confops = &slpi_pinconf_ops;
	pctrldesc->owner = THIS_MODULE;
	pctrldesc->name = dev_name(&pdev->dev);
	pctrldesc->pins = pindesc;
	pctrldesc->npins = npins;

	for (i = 0; i < npins; i++, pindesc++) {
		pin = &pins[i];
		pindesc->drv_data = pin;
		pindesc->number = i;
		pindesc->name = slpi_gpio_groups[i];

		pin->base = base;
		pin->offset = i * 0x1000;
		pin->ctl_reg = 0x0;
		pin->io_reg = 0x4;

		pin->pull_bit = 0;
		pin->out_bit = 1;
		pin->mux_bit = 2;
		pin->oe_bit = 9;
		pin->drv_bit = 6;
		pin->in_bit = 0;
	}

	pctldev = devm_pinctrl_register(&pdev->dev, pctrldesc, NULL);
	if (IS_ERR(pctldev)) {
		dev_err(dev, "Failed to register pinctrl device\n");
		return PTR_ERR(pctldev);
	}

	return 0;
}

static const struct of_device_id slpi_pinctrl_of_match[] = {
	{ .compatible = "qcom,slpi-pinctrl" }, /* Generic */
	{ },
};

MODULE_DEVICE_TABLE(of, slpi_pinctrl_of_match);

static struct platform_driver slpi_pinctrl_driver = {
	.driver = {
		   .name = "qcom-slpi-pinctrl",
		   .of_match_table = slpi_pinctrl_of_match,
	},
	.probe = slpi_pinctrl_probe,
};

static int __init slpi_pinctrl_init(void)
{
	return platform_driver_register(&slpi_pinctrl_driver);
}
arch_initcall(slpi_pinctrl_init);

static void __exit slpi_pinctrl_exit(void)
{
	platform_driver_unregister(&slpi_pinctrl_driver);
}
module_exit(slpi_pinctrl_exit);

MODULE_DESCRIPTION("QTI SLPI GPIO pin control driver");
MODULE_LICENSE("GPL v2");