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