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 28 static 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 45 static 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 59 static 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 79 static 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 103 static 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 111 const 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 }; 118 EXPORT_SYMBOL_GPL(meson_axg_pmx_ops); 119 120 MODULE_LICENSE("Dual BSD/GPL"); 121