18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Second generation of pinmux driver for Amlogic Meson-AXG SoC.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2017 Baylibre SAS.
58c2ecf20Sopenharmony_ci * Author:  Jerome Brunet  <jbrunet@baylibre.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2017 Amlogic, Inc. All rights reserved.
88c2ecf20Sopenharmony_ci * Author: Xingyu Chen <xingyu.chen@amlogic.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * SPDX-License-Identifier: (GPL-2.0+ or MIT)
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * This new generation of pinctrl IP is mainly adopted by the
158c2ecf20Sopenharmony_ci * Meson-AXG SoC and later series, which use 4-width continuous
168c2ecf20Sopenharmony_ci * register bit to select the function for each pin.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * The value 0 is always selecting the GPIO mode, while other
198c2ecf20Sopenharmony_ci * values (start from 1) for selecting the function mode.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci#include <linux/device.h>
228c2ecf20Sopenharmony_ci#include <linux/regmap.h>
238c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
248c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "pinctrl-meson.h"
278c2ecf20Sopenharmony_ci#include "pinctrl-meson-axg-pmx.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int meson_axg_pmx_get_bank(struct meson_pinctrl *pc,
308c2ecf20Sopenharmony_ci			unsigned int pin,
318c2ecf20Sopenharmony_ci			struct meson_pmx_bank **bank)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	int i;
348c2ecf20Sopenharmony_ci	struct meson_axg_pmx_data *pmx = pc->data->pmx_data;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	for (i = 0; i < pmx->num_pmx_banks; i++)
378c2ecf20Sopenharmony_ci		if (pin >= pmx->pmx_banks[i].first &&
388c2ecf20Sopenharmony_ci				pin <= pmx->pmx_banks[i].last) {
398c2ecf20Sopenharmony_ci			*bank = &pmx->pmx_banks[i];
408c2ecf20Sopenharmony_ci			return 0;
418c2ecf20Sopenharmony_ci		}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return -EINVAL;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int meson_pmx_calc_reg_and_offset(struct meson_pmx_bank *bank,
478c2ecf20Sopenharmony_ci			unsigned int pin, unsigned int *reg,
488c2ecf20Sopenharmony_ci			unsigned int *offset)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	int shift;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	shift = pin - bank->first;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	*reg = bank->reg + (bank->offset + (shift << 2)) / 32;
558c2ecf20Sopenharmony_ci	*offset = (bank->offset + (shift << 2)) % 32;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int meson_axg_pmx_update_function(struct meson_pinctrl *pc,
618c2ecf20Sopenharmony_ci			unsigned int pin, unsigned int func)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci	int reg;
658c2ecf20Sopenharmony_ci	int offset;
668c2ecf20Sopenharmony_ci	struct meson_pmx_bank *bank;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	ret = meson_axg_pmx_get_bank(pc, pin, &bank);
698c2ecf20Sopenharmony_ci	if (ret)
708c2ecf20Sopenharmony_ci		return ret;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	meson_pmx_calc_reg_and_offset(bank, pin, &reg, &offset);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pc->reg_mux, reg << 2,
758c2ecf20Sopenharmony_ci		0xf << offset, (func & 0xf) << offset);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return ret;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int meson_axg_pmx_set_mux(struct pinctrl_dev *pcdev,
818c2ecf20Sopenharmony_ci			unsigned int func_num, unsigned int group_num)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	int i;
848c2ecf20Sopenharmony_ci	int ret;
858c2ecf20Sopenharmony_ci	struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
868c2ecf20Sopenharmony_ci	struct meson_pmx_func *func = &pc->data->funcs[func_num];
878c2ecf20Sopenharmony_ci	struct meson_pmx_group *group = &pc->data->groups[group_num];
888c2ecf20Sopenharmony_ci	struct meson_pmx_axg_data *pmx_data =
898c2ecf20Sopenharmony_ci		(struct meson_pmx_axg_data *)group->data;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	dev_dbg(pc->dev, "enable function %s, group %s\n", func->name,
928c2ecf20Sopenharmony_ci		group->name);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	for (i = 0; i < group->num_pins; i++) {
958c2ecf20Sopenharmony_ci		ret = meson_axg_pmx_update_function(pc, group->pins[i],
968c2ecf20Sopenharmony_ci			pmx_data->func);
978c2ecf20Sopenharmony_ci		if (ret)
988c2ecf20Sopenharmony_ci			return ret;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int meson_axg_pmx_request_gpio(struct pinctrl_dev *pcdev,
1058c2ecf20Sopenharmony_ci			struct pinctrl_gpio_range *range, unsigned int offset)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return meson_axg_pmx_update_function(pc, offset, 0);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciconst struct pinmux_ops meson_axg_pmx_ops = {
1138c2ecf20Sopenharmony_ci	.set_mux = meson_axg_pmx_set_mux,
1148c2ecf20Sopenharmony_ci	.get_functions_count = meson_pmx_get_funcs_count,
1158c2ecf20Sopenharmony_ci	.get_function_name = meson_pmx_get_func_name,
1168c2ecf20Sopenharmony_ci	.get_function_groups = meson_pmx_get_groups,
1178c2ecf20Sopenharmony_ci	.gpio_request_enable = meson_axg_pmx_request_gpio,
1188c2ecf20Sopenharmony_ci};
119