1// SPDX-License-Identifier: (GPL-2.0+ OR MIT) 2/* 3 * Second generation of pinmux driver for Amlogic Meson-AXG SoC. 4 * 5 * Copyright (c) 2017 Baylibre SAS. 6 * Author: Jerome Brunet <jbrunet@baylibre.com> 7 * 8 * Copyright (c) 2017 Amlogic, Inc. All rights reserved. 9 * Author: Xingyu Chen <xingyu.chen@amlogic.com> 10 */ 11 12/* 13 * This new generation of pinctrl IP is mainly adopted by the 14 * Meson-AXG SoC and later series, which use 4-width continuous 15 * register bit to select the function for each pin. 16 * 17 * The value 0 is always selecting the GPIO mode, while other 18 * values (start from 1) for selecting the function mode. 19 */ 20#include <linux/device.h> 21#include <linux/regmap.h> 22#include <linux/pinctrl/pinctrl.h> 23#include <linux/pinctrl/pinmux.h> 24 25#include "pinctrl-meson.h" 26#include "pinctrl-meson-axg-pmx.h" 27 28static int meson_axg_pmx_get_bank(struct meson_pinctrl *pc, 29 unsigned int pin, 30 struct meson_pmx_bank **bank) 31{ 32 int i; 33 struct meson_axg_pmx_data *pmx = pc->data->pmx_data; 34 35 for (i = 0; i < pmx->num_pmx_banks; i++) 36 if (pin >= pmx->pmx_banks[i].first && 37 pin <= pmx->pmx_banks[i].last) { 38 *bank = &pmx->pmx_banks[i]; 39 return 0; 40 } 41 42 return -EINVAL; 43} 44 45static int meson_pmx_calc_reg_and_offset(struct meson_pmx_bank *bank, 46 unsigned int pin, unsigned int *reg, 47 unsigned int *offset) 48{ 49 int shift; 50 51 shift = pin - bank->first; 52 53 *reg = bank->reg + (bank->offset + (shift << 2)) / 32; 54 *offset = (bank->offset + (shift << 2)) % 32; 55 56 return 0; 57} 58 59static int meson_axg_pmx_update_function(struct meson_pinctrl *pc, 60 unsigned int pin, unsigned int func) 61{ 62 int ret; 63 int reg; 64 int offset; 65 struct meson_pmx_bank *bank; 66 67 ret = meson_axg_pmx_get_bank(pc, pin, &bank); 68 if (ret) 69 return ret; 70 71 meson_pmx_calc_reg_and_offset(bank, pin, ®, &offset); 72 73 ret = regmap_update_bits(pc->reg_mux, reg << 2, 74 0xf << offset, (func & 0xf) << offset); 75 76 return ret; 77} 78 79static int meson_axg_pmx_set_mux(struct pinctrl_dev *pcdev, 80 unsigned int func_num, unsigned int group_num) 81{ 82 int i; 83 int ret; 84 struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev); 85 struct meson_pmx_func *func = &pc->data->funcs[func_num]; 86 struct meson_pmx_group *group = &pc->data->groups[group_num]; 87 struct meson_pmx_axg_data *pmx_data = 88 (struct meson_pmx_axg_data *)group->data; 89 90 dev_dbg(pc->dev, "enable function %s, group %s\n", func->name, 91 group->name); 92 93 for (i = 0; i < group->num_pins; i++) { 94 ret = meson_axg_pmx_update_function(pc, group->pins[i], 95 pmx_data->func); 96 if (ret) 97 return ret; 98 } 99 100 return 0; 101} 102 103static int meson_axg_pmx_request_gpio(struct pinctrl_dev *pcdev, 104 struct pinctrl_gpio_range *range, unsigned int offset) 105{ 106 struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev); 107 108 return meson_axg_pmx_update_function(pc, offset, 0); 109} 110 111const struct pinmux_ops meson_axg_pmx_ops = { 112 .set_mux = meson_axg_pmx_set_mux, 113 .get_functions_count = meson_pmx_get_funcs_count, 114 .get_function_name = meson_pmx_get_func_name, 115 .get_function_groups = meson_pmx_get_groups, 116 .gpio_request_enable = meson_axg_pmx_request_gpio, 117}; 118EXPORT_SYMBOL_GPL(meson_axg_pmx_ops); 119 120MODULE_LICENSE("Dual BSD/GPL"); 121