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, ®, &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