18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OWL SoC's Pinctrl driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Actions Semi Inc. 68c2ecf20Sopenharmony_ci * Author: David Liu <liuwei@actions-semi.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2018 Linaro Ltd. 98c2ecf20Sopenharmony_ci * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/irq.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/pinctrl/machine.h> 218c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 228c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 238c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 248c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "../core.h" 298c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h" 308c2ecf20Sopenharmony_ci#include "pinctrl-owl.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/** 338c2ecf20Sopenharmony_ci * struct owl_pinctrl - pinctrl state of the device 348c2ecf20Sopenharmony_ci * @dev: device handle 358c2ecf20Sopenharmony_ci * @pctrldev: pinctrl handle 368c2ecf20Sopenharmony_ci * @chip: gpio chip 378c2ecf20Sopenharmony_ci * @lock: spinlock to protect registers 388c2ecf20Sopenharmony_ci * @clk: clock control 398c2ecf20Sopenharmony_ci * @soc: reference to soc_data 408c2ecf20Sopenharmony_ci * @base: pinctrl register base address 418c2ecf20Sopenharmony_ci * @irq_chip: IRQ chip information 428c2ecf20Sopenharmony_ci * @num_irq: number of possible interrupts 438c2ecf20Sopenharmony_ci * @irq: interrupt numbers 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistruct owl_pinctrl { 468c2ecf20Sopenharmony_ci struct device *dev; 478c2ecf20Sopenharmony_ci struct pinctrl_dev *pctrldev; 488c2ecf20Sopenharmony_ci struct gpio_chip chip; 498c2ecf20Sopenharmony_ci raw_spinlock_t lock; 508c2ecf20Sopenharmony_ci struct clk *clk; 518c2ecf20Sopenharmony_ci const struct owl_pinctrl_soc_data *soc; 528c2ecf20Sopenharmony_ci void __iomem *base; 538c2ecf20Sopenharmony_ci struct irq_chip irq_chip; 548c2ecf20Sopenharmony_ci unsigned int num_irq; 558c2ecf20Sopenharmony_ci unsigned int *irq; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void owl_update_bits(void __iomem *base, u32 mask, u32 val) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci u32 reg_val; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci reg_val = readl_relaxed(base); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci reg_val = (reg_val & ~mask) | (val & mask); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci writel_relaxed(reg_val, base); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic u32 owl_read_field(struct owl_pinctrl *pctrl, u32 reg, 708c2ecf20Sopenharmony_ci u32 bit, u32 width) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u32 tmp, mask; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci tmp = readl_relaxed(pctrl->base + reg); 758c2ecf20Sopenharmony_ci mask = (1 << width) - 1; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return (tmp >> bit) & mask; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void owl_write_field(struct owl_pinctrl *pctrl, u32 reg, u32 arg, 818c2ecf20Sopenharmony_ci u32 bit, u32 width) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u32 mask; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mask = (1 << width) - 1; 868c2ecf20Sopenharmony_ci mask = mask << bit; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci owl_update_bits(pctrl->base + reg, mask, (arg << bit)); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int owl_get_groups_count(struct pinctrl_dev *pctrldev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return pctrl->soc->ngroups; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const char *owl_get_group_name(struct pinctrl_dev *pctrldev, 998c2ecf20Sopenharmony_ci unsigned int group) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return pctrl->soc->groups[group].name; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int owl_get_group_pins(struct pinctrl_dev *pctrldev, 1078c2ecf20Sopenharmony_ci unsigned int group, 1088c2ecf20Sopenharmony_ci const unsigned int **pins, 1098c2ecf20Sopenharmony_ci unsigned int *num_pins) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci *pins = pctrl->soc->groups[group].pads; 1148c2ecf20Sopenharmony_ci *num_pins = pctrl->soc->groups[group].npads; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void owl_pin_dbg_show(struct pinctrl_dev *pctrldev, 1208c2ecf20Sopenharmony_ci struct seq_file *s, 1218c2ecf20Sopenharmony_ci unsigned int offset) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci seq_printf(s, "%s", dev_name(pctrl->dev)); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct pinctrl_ops owl_pinctrl_ops = { 1298c2ecf20Sopenharmony_ci .get_groups_count = owl_get_groups_count, 1308c2ecf20Sopenharmony_ci .get_group_name = owl_get_group_name, 1318c2ecf20Sopenharmony_ci .get_group_pins = owl_get_group_pins, 1328c2ecf20Sopenharmony_ci .pin_dbg_show = owl_pin_dbg_show, 1338c2ecf20Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_all, 1348c2ecf20Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int owl_get_funcs_count(struct pinctrl_dev *pctrldev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return pctrl->soc->nfunctions; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic const char *owl_get_func_name(struct pinctrl_dev *pctrldev, 1458c2ecf20Sopenharmony_ci unsigned int function) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return pctrl->soc->functions[function].name; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int owl_get_func_groups(struct pinctrl_dev *pctrldev, 1538c2ecf20Sopenharmony_ci unsigned int function, 1548c2ecf20Sopenharmony_ci const char * const **groups, 1558c2ecf20Sopenharmony_ci unsigned int * const num_groups) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci *groups = pctrl->soc->functions[function].groups; 1608c2ecf20Sopenharmony_ci *num_groups = pctrl->soc->functions[function].ngroups; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic inline int get_group_mfp_mask_val(const struct owl_pingroup *g, 1668c2ecf20Sopenharmony_ci int function, 1678c2ecf20Sopenharmony_ci u32 *mask, 1688c2ecf20Sopenharmony_ci u32 *val) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int id; 1718c2ecf20Sopenharmony_ci u32 option_num; 1728c2ecf20Sopenharmony_ci u32 option_mask; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci for (id = 0; id < g->nfuncs; id++) { 1758c2ecf20Sopenharmony_ci if (g->funcs[id] == function) 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci if (WARN_ON(id == g->nfuncs)) 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci option_num = (1 << g->mfpctl_width); 1828c2ecf20Sopenharmony_ci if (id > option_num) 1838c2ecf20Sopenharmony_ci id -= option_num; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci option_mask = option_num - 1; 1868c2ecf20Sopenharmony_ci *mask = (option_mask << g->mfpctl_shift); 1878c2ecf20Sopenharmony_ci *val = (id << g->mfpctl_shift); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int owl_set_mux(struct pinctrl_dev *pctrldev, 1938c2ecf20Sopenharmony_ci unsigned int function, 1948c2ecf20Sopenharmony_ci unsigned int group) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 1978c2ecf20Sopenharmony_ci const struct owl_pingroup *g; 1988c2ecf20Sopenharmony_ci unsigned long flags; 1998c2ecf20Sopenharmony_ci u32 val, mask; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci g = &pctrl->soc->groups[group]; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (get_group_mfp_mask_val(g, function, &mask, &val)) 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci owl_update_bits(pctrl->base + g->mfpctl_reg, mask, val); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic const struct pinmux_ops owl_pinmux_ops = { 2168c2ecf20Sopenharmony_ci .get_functions_count = owl_get_funcs_count, 2178c2ecf20Sopenharmony_ci .get_function_name = owl_get_func_name, 2188c2ecf20Sopenharmony_ci .get_function_groups = owl_get_func_groups, 2198c2ecf20Sopenharmony_ci .set_mux = owl_set_mux, 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int owl_pad_pinconf_reg(const struct owl_padinfo *info, 2238c2ecf20Sopenharmony_ci unsigned int param, 2248c2ecf20Sopenharmony_ci u32 *reg, 2258c2ecf20Sopenharmony_ci u32 *bit, 2268c2ecf20Sopenharmony_ci u32 *width) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci switch (param) { 2298c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_BUS_HOLD: 2308c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 2318c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 2328c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 2338c2ecf20Sopenharmony_ci if (!info->pullctl) 2348c2ecf20Sopenharmony_ci return -EINVAL; 2358c2ecf20Sopenharmony_ci *reg = info->pullctl->reg; 2368c2ecf20Sopenharmony_ci *bit = info->pullctl->shift; 2378c2ecf20Sopenharmony_ci *width = info->pullctl->width; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_SCHMITT_ENABLE: 2408c2ecf20Sopenharmony_ci if (!info->st) 2418c2ecf20Sopenharmony_ci return -EINVAL; 2428c2ecf20Sopenharmony_ci *reg = info->st->reg; 2438c2ecf20Sopenharmony_ci *bit = info->st->shift; 2448c2ecf20Sopenharmony_ci *width = info->st->width; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci default: 2478c2ecf20Sopenharmony_ci return -ENOTSUPP; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int owl_pin_config_get(struct pinctrl_dev *pctrldev, 2548c2ecf20Sopenharmony_ci unsigned int pin, 2558c2ecf20Sopenharmony_ci unsigned long *config) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci int ret = 0; 2588c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 2598c2ecf20Sopenharmony_ci const struct owl_padinfo *info; 2608c2ecf20Sopenharmony_ci unsigned int param = pinconf_to_config_param(*config); 2618c2ecf20Sopenharmony_ci u32 reg, bit, width, arg; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci info = &pctrl->soc->padinfo[pin]; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = owl_pad_pinconf_reg(info, param, ®, &bit, &width); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci arg = owl_read_field(pctrl, reg, bit, width); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (!pctrl->soc->padctl_val2arg) 2728c2ecf20Sopenharmony_ci return -ENOTSUPP; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = pctrl->soc->padctl_val2arg(info, param, &arg); 2758c2ecf20Sopenharmony_ci if (ret) 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return ret; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int owl_pin_config_set(struct pinctrl_dev *pctrldev, 2848c2ecf20Sopenharmony_ci unsigned int pin, 2858c2ecf20Sopenharmony_ci unsigned long *configs, 2868c2ecf20Sopenharmony_ci unsigned int num_configs) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 2898c2ecf20Sopenharmony_ci const struct owl_padinfo *info; 2908c2ecf20Sopenharmony_ci unsigned long flags; 2918c2ecf20Sopenharmony_ci unsigned int param; 2928c2ecf20Sopenharmony_ci u32 reg, bit, width, arg; 2938c2ecf20Sopenharmony_ci int ret = 0, i; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci info = &pctrl->soc->padinfo[pin]; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 2988c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 2998c2ecf20Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = owl_pad_pinconf_reg(info, param, ®, &bit, &width); 3028c2ecf20Sopenharmony_ci if (ret) 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (!pctrl->soc->padctl_arg2val) 3068c2ecf20Sopenharmony_ci return -ENOTSUPP; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = pctrl->soc->padctl_arg2val(info, param, &arg); 3098c2ecf20Sopenharmony_ci if (ret) 3108c2ecf20Sopenharmony_ci return ret; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci owl_write_field(pctrl, reg, arg, bit, width); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int owl_group_pinconf_reg(const struct owl_pingroup *g, 3238c2ecf20Sopenharmony_ci unsigned int param, 3248c2ecf20Sopenharmony_ci u32 *reg, 3258c2ecf20Sopenharmony_ci u32 *bit, 3268c2ecf20Sopenharmony_ci u32 *width) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci switch (param) { 3298c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 3308c2ecf20Sopenharmony_ci if (g->drv_reg < 0) 3318c2ecf20Sopenharmony_ci return -EINVAL; 3328c2ecf20Sopenharmony_ci *reg = g->drv_reg; 3338c2ecf20Sopenharmony_ci *bit = g->drv_shift; 3348c2ecf20Sopenharmony_ci *width = g->drv_width; 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case PIN_CONFIG_SLEW_RATE: 3378c2ecf20Sopenharmony_ci if (g->sr_reg < 0) 3388c2ecf20Sopenharmony_ci return -EINVAL; 3398c2ecf20Sopenharmony_ci *reg = g->sr_reg; 3408c2ecf20Sopenharmony_ci *bit = g->sr_shift; 3418c2ecf20Sopenharmony_ci *width = g->sr_width; 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci default: 3448c2ecf20Sopenharmony_ci return -ENOTSUPP; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int owl_group_pinconf_arg2val(const struct owl_pingroup *g, 3518c2ecf20Sopenharmony_ci unsigned int param, 3528c2ecf20Sopenharmony_ci u32 *arg) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci switch (param) { 3558c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 3568c2ecf20Sopenharmony_ci switch (*arg) { 3578c2ecf20Sopenharmony_ci case 2: 3588c2ecf20Sopenharmony_ci *arg = OWL_PINCONF_DRV_2MA; 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case 4: 3618c2ecf20Sopenharmony_ci *arg = OWL_PINCONF_DRV_4MA; 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci case 8: 3648c2ecf20Sopenharmony_ci *arg = OWL_PINCONF_DRV_8MA; 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case 12: 3678c2ecf20Sopenharmony_ci *arg = OWL_PINCONF_DRV_12MA; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci default: 3708c2ecf20Sopenharmony_ci return -EINVAL; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci case PIN_CONFIG_SLEW_RATE: 3748c2ecf20Sopenharmony_ci if (*arg) 3758c2ecf20Sopenharmony_ci *arg = OWL_PINCONF_SLEW_FAST; 3768c2ecf20Sopenharmony_ci else 3778c2ecf20Sopenharmony_ci *arg = OWL_PINCONF_SLEW_SLOW; 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci default: 3808c2ecf20Sopenharmony_ci return -ENOTSUPP; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int owl_group_pinconf_val2arg(const struct owl_pingroup *g, 3878c2ecf20Sopenharmony_ci unsigned int param, 3888c2ecf20Sopenharmony_ci u32 *arg) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci switch (param) { 3918c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 3928c2ecf20Sopenharmony_ci switch (*arg) { 3938c2ecf20Sopenharmony_ci case OWL_PINCONF_DRV_2MA: 3948c2ecf20Sopenharmony_ci *arg = 2; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci case OWL_PINCONF_DRV_4MA: 3978c2ecf20Sopenharmony_ci *arg = 4; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case OWL_PINCONF_DRV_8MA: 4008c2ecf20Sopenharmony_ci *arg = 8; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case OWL_PINCONF_DRV_12MA: 4038c2ecf20Sopenharmony_ci *arg = 12; 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci default: 4068c2ecf20Sopenharmony_ci return -EINVAL; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci case PIN_CONFIG_SLEW_RATE: 4108c2ecf20Sopenharmony_ci if (*arg) 4118c2ecf20Sopenharmony_ci *arg = 1; 4128c2ecf20Sopenharmony_ci else 4138c2ecf20Sopenharmony_ci *arg = 0; 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci default: 4168c2ecf20Sopenharmony_ci return -ENOTSUPP; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int owl_group_config_get(struct pinctrl_dev *pctrldev, 4238c2ecf20Sopenharmony_ci unsigned int group, 4248c2ecf20Sopenharmony_ci unsigned long *config) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci const struct owl_pingroup *g; 4278c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 4288c2ecf20Sopenharmony_ci unsigned int param = pinconf_to_config_param(*config); 4298c2ecf20Sopenharmony_ci u32 reg, bit, width, arg; 4308c2ecf20Sopenharmony_ci int ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci g = &pctrl->soc->groups[group]; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci ret = owl_group_pinconf_reg(g, param, ®, &bit, &width); 4358c2ecf20Sopenharmony_ci if (ret) 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci arg = owl_read_field(pctrl, reg, bit, width); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ret = owl_group_pinconf_val2arg(g, param, &arg); 4418c2ecf20Sopenharmony_ci if (ret) 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return ret; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int owl_group_config_set(struct pinctrl_dev *pctrldev, 4508c2ecf20Sopenharmony_ci unsigned int group, 4518c2ecf20Sopenharmony_ci unsigned long *configs, 4528c2ecf20Sopenharmony_ci unsigned int num_configs) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci const struct owl_pingroup *g; 4558c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); 4568c2ecf20Sopenharmony_ci unsigned long flags; 4578c2ecf20Sopenharmony_ci unsigned int param; 4588c2ecf20Sopenharmony_ci u32 reg, bit, width, arg; 4598c2ecf20Sopenharmony_ci int ret, i; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci g = &pctrl->soc->groups[group]; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 4648c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 4658c2ecf20Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ret = owl_group_pinconf_reg(g, param, ®, &bit, &width); 4688c2ecf20Sopenharmony_ci if (ret) 4698c2ecf20Sopenharmony_ci return ret; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ret = owl_group_pinconf_arg2val(g, param, &arg); 4728c2ecf20Sopenharmony_ci if (ret) 4738c2ecf20Sopenharmony_ci return ret; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Update register */ 4768c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci owl_write_field(pctrl, reg, arg, bit, width); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return 0; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic const struct pinconf_ops owl_pinconf_ops = { 4878c2ecf20Sopenharmony_ci .is_generic = true, 4888c2ecf20Sopenharmony_ci .pin_config_get = owl_pin_config_get, 4898c2ecf20Sopenharmony_ci .pin_config_set = owl_pin_config_set, 4908c2ecf20Sopenharmony_ci .pin_config_group_get = owl_group_config_get, 4918c2ecf20Sopenharmony_ci .pin_config_group_set = owl_group_config_set, 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic struct pinctrl_desc owl_pinctrl_desc = { 4958c2ecf20Sopenharmony_ci .pctlops = &owl_pinctrl_ops, 4968c2ecf20Sopenharmony_ci .pmxops = &owl_pinmux_ops, 4978c2ecf20Sopenharmony_ci .confops = &owl_pinconf_ops, 4988c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic const struct owl_gpio_port * 5028c2ecf20Sopenharmony_ciowl_gpio_get_port(struct owl_pinctrl *pctrl, unsigned int *pin) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci unsigned int start = 0, i; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci for (i = 0; i < pctrl->soc->nports; i++) { 5078c2ecf20Sopenharmony_ci const struct owl_gpio_port *port = &pctrl->soc->ports[i]; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (*pin >= start && *pin < start + port->pins) { 5108c2ecf20Sopenharmony_ci *pin -= start; 5118c2ecf20Sopenharmony_ci return port; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci start += port->pins; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return NULL; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void owl_gpio_update_reg(void __iomem *base, unsigned int pin, int flag) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci u32 val; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci val = readl_relaxed(base); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (flag) 5278c2ecf20Sopenharmony_ci val |= BIT(pin); 5288c2ecf20Sopenharmony_ci else 5298c2ecf20Sopenharmony_ci val &= ~BIT(pin); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci writel_relaxed(val, base); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int owl_gpio_request(struct gpio_chip *chip, unsigned int offset) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(chip); 5378c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 5388c2ecf20Sopenharmony_ci void __iomem *gpio_base; 5398c2ecf20Sopenharmony_ci unsigned long flags; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &offset); 5428c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 5438c2ecf20Sopenharmony_ci return -ENODEV; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* 5488c2ecf20Sopenharmony_ci * GPIOs have higher priority over other modules, so either setting 5498c2ecf20Sopenharmony_ci * them as OUT or IN is sufficient 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 5528c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->outen, offset, true); 5538c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic void owl_gpio_free(struct gpio_chip *chip, unsigned int offset) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(chip); 5618c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 5628c2ecf20Sopenharmony_ci void __iomem *gpio_base; 5638c2ecf20Sopenharmony_ci unsigned long flags; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &offset); 5668c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 5678c2ecf20Sopenharmony_ci return; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 5728c2ecf20Sopenharmony_ci /* disable gpio output */ 5738c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->outen, offset, false); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* disable gpio input */ 5768c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->inen, offset, false); 5778c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int owl_gpio_get(struct gpio_chip *chip, unsigned int offset) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(chip); 5838c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 5848c2ecf20Sopenharmony_ci void __iomem *gpio_base; 5858c2ecf20Sopenharmony_ci unsigned long flags; 5868c2ecf20Sopenharmony_ci u32 val; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &offset); 5898c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 5908c2ecf20Sopenharmony_ci return -ENODEV; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 5958c2ecf20Sopenharmony_ci val = readl_relaxed(gpio_base + port->dat); 5968c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return !!(val & BIT(offset)); 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic void owl_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(chip); 6048c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 6058c2ecf20Sopenharmony_ci void __iomem *gpio_base; 6068c2ecf20Sopenharmony_ci unsigned long flags; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &offset); 6098c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 6108c2ecf20Sopenharmony_ci return; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 6158c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->dat, offset, value); 6168c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int owl_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(chip); 6228c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 6238c2ecf20Sopenharmony_ci void __iomem *gpio_base; 6248c2ecf20Sopenharmony_ci unsigned long flags; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &offset); 6278c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 6288c2ecf20Sopenharmony_ci return -ENODEV; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 6338c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->outen, offset, false); 6348c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->inen, offset, true); 6358c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci return 0; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int owl_gpio_direction_output(struct gpio_chip *chip, 6418c2ecf20Sopenharmony_ci unsigned int offset, int value) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(chip); 6448c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 6458c2ecf20Sopenharmony_ci void __iomem *gpio_base; 6468c2ecf20Sopenharmony_ci unsigned long flags; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &offset); 6498c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 6508c2ecf20Sopenharmony_ci return -ENODEV; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 6558c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->inen, offset, false); 6568c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->outen, offset, true); 6578c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->dat, offset, value); 6588c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return 0; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic void irq_set_type(struct owl_pinctrl *pctrl, int gpio, unsigned int type) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 6668c2ecf20Sopenharmony_ci void __iomem *gpio_base; 6678c2ecf20Sopenharmony_ci unsigned long flags; 6688c2ecf20Sopenharmony_ci unsigned int offset, value, irq_type = 0; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci switch (type) { 6718c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 6728c2ecf20Sopenharmony_ci /* 6738c2ecf20Sopenharmony_ci * Since the hardware doesn't support interrupts on both edges, 6748c2ecf20Sopenharmony_ci * emulate it in the software by setting the single edge 6758c2ecf20Sopenharmony_ci * interrupt and switching to the opposite edge while ACKing 6768c2ecf20Sopenharmony_ci * the interrupt 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_ci if (owl_gpio_get(&pctrl->chip, gpio)) 6798c2ecf20Sopenharmony_ci irq_type = OWL_GPIO_INT_EDGE_FALLING; 6808c2ecf20Sopenharmony_ci else 6818c2ecf20Sopenharmony_ci irq_type = OWL_GPIO_INT_EDGE_RISING; 6828c2ecf20Sopenharmony_ci break; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 6858c2ecf20Sopenharmony_ci irq_type = OWL_GPIO_INT_EDGE_RISING; 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 6898c2ecf20Sopenharmony_ci irq_type = OWL_GPIO_INT_EDGE_FALLING; 6908c2ecf20Sopenharmony_ci break; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 6938c2ecf20Sopenharmony_ci irq_type = OWL_GPIO_INT_LEVEL_HIGH; 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 6978c2ecf20Sopenharmony_ci irq_type = OWL_GPIO_INT_LEVEL_LOW; 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci default: 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &gpio); 7058c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 7068c2ecf20Sopenharmony_ci return; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci offset = (gpio < 16) ? 4 : 0; 7138c2ecf20Sopenharmony_ci value = readl_relaxed(gpio_base + port->intc_type + offset); 7148c2ecf20Sopenharmony_ci value &= ~(OWL_GPIO_INT_MASK << ((gpio % 16) * 2)); 7158c2ecf20Sopenharmony_ci value |= irq_type << ((gpio % 16) * 2); 7168c2ecf20Sopenharmony_ci writel_relaxed(value, gpio_base + port->intc_type + offset); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic void owl_gpio_irq_mask(struct irq_data *data) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 7248c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(gc); 7258c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 7268c2ecf20Sopenharmony_ci void __iomem *gpio_base; 7278c2ecf20Sopenharmony_ci unsigned long flags; 7288c2ecf20Sopenharmony_ci unsigned int gpio = data->hwirq; 7298c2ecf20Sopenharmony_ci u32 val; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &gpio); 7328c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 7338c2ecf20Sopenharmony_ci return; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->intc_msk, gpio, false); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* disable port interrupt if no interrupt pending bit is active */ 7428c2ecf20Sopenharmony_ci val = readl_relaxed(gpio_base + port->intc_msk); 7438c2ecf20Sopenharmony_ci if (val == 0) 7448c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->intc_ctl, 7458c2ecf20Sopenharmony_ci OWL_GPIO_CTLR_ENABLE + port->shared_ctl_offset * 5, false); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic void owl_gpio_irq_unmask(struct irq_data *data) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 7538c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(gc); 7548c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 7558c2ecf20Sopenharmony_ci void __iomem *gpio_base; 7568c2ecf20Sopenharmony_ci unsigned long flags; 7578c2ecf20Sopenharmony_ci unsigned int gpio = data->hwirq; 7588c2ecf20Sopenharmony_ci u32 value; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &gpio); 7618c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 7628c2ecf20Sopenharmony_ci return; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 7658c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* enable port interrupt */ 7688c2ecf20Sopenharmony_ci value = readl_relaxed(gpio_base + port->intc_ctl); 7698c2ecf20Sopenharmony_ci value |= ((BIT(OWL_GPIO_CTLR_ENABLE) | BIT(OWL_GPIO_CTLR_SAMPLE_CLK_24M)) 7708c2ecf20Sopenharmony_ci << port->shared_ctl_offset * 5); 7718c2ecf20Sopenharmony_ci writel_relaxed(value, gpio_base + port->intc_ctl); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* enable GPIO interrupt */ 7748c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->intc_msk, gpio, true); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic void owl_gpio_irq_ack(struct irq_data *data) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 7828c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(gc); 7838c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 7848c2ecf20Sopenharmony_ci void __iomem *gpio_base; 7858c2ecf20Sopenharmony_ci unsigned long flags; 7868c2ecf20Sopenharmony_ci unsigned int gpio = data->hwirq; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* 7898c2ecf20Sopenharmony_ci * Switch the interrupt edge to the opposite edge of the interrupt 7908c2ecf20Sopenharmony_ci * which got triggered for the case of emulating both edges 7918c2ecf20Sopenharmony_ci */ 7928c2ecf20Sopenharmony_ci if (irqd_get_trigger_type(data) == IRQ_TYPE_EDGE_BOTH) { 7938c2ecf20Sopenharmony_ci if (owl_gpio_get(gc, gpio)) 7948c2ecf20Sopenharmony_ci irq_set_type(pctrl, gpio, IRQ_TYPE_EDGE_FALLING); 7958c2ecf20Sopenharmony_ci else 7968c2ecf20Sopenharmony_ci irq_set_type(pctrl, gpio, IRQ_TYPE_EDGE_RISING); 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci port = owl_gpio_get_port(pctrl, &gpio); 8008c2ecf20Sopenharmony_ci if (WARN_ON(port == NULL)) 8018c2ecf20Sopenharmony_ci return; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci gpio_base = pctrl->base + port->offset; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pctrl->lock, flags); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci owl_gpio_update_reg(gpio_base + port->intc_ctl, 8088c2ecf20Sopenharmony_ci OWL_GPIO_CTLR_PENDING + port->shared_ctl_offset * 5, true); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pctrl->lock, flags); 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int owl_gpio_irq_set_type(struct irq_data *data, unsigned int type) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 8168c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = gpiochip_get_data(gc); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) 8198c2ecf20Sopenharmony_ci irq_set_handler_locked(data, handle_level_irq); 8208c2ecf20Sopenharmony_ci else 8218c2ecf20Sopenharmony_ci irq_set_handler_locked(data, handle_edge_irq); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci irq_set_type(pctrl, data->hwirq, type); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci return 0; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic void owl_gpio_irq_handler(struct irq_desc *desc) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl = irq_desc_get_handler_data(desc); 8318c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 8328c2ecf20Sopenharmony_ci struct irq_domain *domain = pctrl->chip.irq.domain; 8338c2ecf20Sopenharmony_ci unsigned int parent = irq_desc_get_irq(desc); 8348c2ecf20Sopenharmony_ci const struct owl_gpio_port *port; 8358c2ecf20Sopenharmony_ci void __iomem *base; 8368c2ecf20Sopenharmony_ci unsigned int pin, irq, offset = 0, i; 8378c2ecf20Sopenharmony_ci unsigned long pending_irq; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci for (i = 0; i < pctrl->soc->nports; i++) { 8428c2ecf20Sopenharmony_ci port = &pctrl->soc->ports[i]; 8438c2ecf20Sopenharmony_ci base = pctrl->base + port->offset; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* skip ports that are not associated with this irq */ 8468c2ecf20Sopenharmony_ci if (parent != pctrl->irq[i]) 8478c2ecf20Sopenharmony_ci goto skip; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci pending_irq = readl_relaxed(base + port->intc_pd); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci for_each_set_bit(pin, &pending_irq, port->pins) { 8528c2ecf20Sopenharmony_ci irq = irq_find_mapping(domain, offset + pin); 8538c2ecf20Sopenharmony_ci generic_handle_irq(irq); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* clear pending interrupt */ 8568c2ecf20Sopenharmony_ci owl_gpio_update_reg(base + port->intc_pd, pin, true); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ciskip: 8608c2ecf20Sopenharmony_ci offset += port->pins; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int owl_gpio_init(struct owl_pinctrl *pctrl) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct gpio_chip *chip; 8698c2ecf20Sopenharmony_ci struct gpio_irq_chip *gpio_irq; 8708c2ecf20Sopenharmony_ci int ret, i, j, offset; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci chip = &pctrl->chip; 8738c2ecf20Sopenharmony_ci chip->base = -1; 8748c2ecf20Sopenharmony_ci chip->ngpio = pctrl->soc->ngpios; 8758c2ecf20Sopenharmony_ci chip->label = dev_name(pctrl->dev); 8768c2ecf20Sopenharmony_ci chip->parent = pctrl->dev; 8778c2ecf20Sopenharmony_ci chip->owner = THIS_MODULE; 8788c2ecf20Sopenharmony_ci chip->of_node = pctrl->dev->of_node; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci pctrl->irq_chip.name = chip->of_node->name; 8818c2ecf20Sopenharmony_ci pctrl->irq_chip.irq_ack = owl_gpio_irq_ack; 8828c2ecf20Sopenharmony_ci pctrl->irq_chip.irq_mask = owl_gpio_irq_mask; 8838c2ecf20Sopenharmony_ci pctrl->irq_chip.irq_unmask = owl_gpio_irq_unmask; 8848c2ecf20Sopenharmony_ci pctrl->irq_chip.irq_set_type = owl_gpio_irq_set_type; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci gpio_irq = &chip->irq; 8878c2ecf20Sopenharmony_ci gpio_irq->chip = &pctrl->irq_chip; 8888c2ecf20Sopenharmony_ci gpio_irq->handler = handle_simple_irq; 8898c2ecf20Sopenharmony_ci gpio_irq->default_type = IRQ_TYPE_NONE; 8908c2ecf20Sopenharmony_ci gpio_irq->parent_handler = owl_gpio_irq_handler; 8918c2ecf20Sopenharmony_ci gpio_irq->parent_handler_data = pctrl; 8928c2ecf20Sopenharmony_ci gpio_irq->num_parents = pctrl->num_irq; 8938c2ecf20Sopenharmony_ci gpio_irq->parents = pctrl->irq; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci gpio_irq->map = devm_kcalloc(pctrl->dev, chip->ngpio, 8968c2ecf20Sopenharmony_ci sizeof(*gpio_irq->map), GFP_KERNEL); 8978c2ecf20Sopenharmony_ci if (!gpio_irq->map) 8988c2ecf20Sopenharmony_ci return -ENOMEM; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci for (i = 0, offset = 0; i < pctrl->soc->nports; i++) { 9018c2ecf20Sopenharmony_ci const struct owl_gpio_port *port = &pctrl->soc->ports[i]; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci for (j = 0; j < port->pins; j++) 9048c2ecf20Sopenharmony_ci gpio_irq->map[offset + j] = gpio_irq->parents[i]; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci offset += port->pins; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&pctrl->chip, pctrl); 9108c2ecf20Sopenharmony_ci if (ret) { 9118c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to register gpiochip\n"); 9128c2ecf20Sopenharmony_ci return ret; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ciint owl_pinctrl_probe(struct platform_device *pdev, 9198c2ecf20Sopenharmony_ci struct owl_pinctrl_soc_data *soc_data) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct owl_pinctrl *pctrl; 9228c2ecf20Sopenharmony_ci int ret, i; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); 9258c2ecf20Sopenharmony_ci if (!pctrl) 9268c2ecf20Sopenharmony_ci return -ENOMEM; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci pctrl->base = devm_platform_ioremap_resource(pdev, 0); 9298c2ecf20Sopenharmony_ci if (IS_ERR(pctrl->base)) 9308c2ecf20Sopenharmony_ci return PTR_ERR(pctrl->base); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* enable GPIO/MFP clock */ 9338c2ecf20Sopenharmony_ci pctrl->clk = devm_clk_get(&pdev->dev, NULL); 9348c2ecf20Sopenharmony_ci if (IS_ERR(pctrl->clk)) { 9358c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no clock defined\n"); 9368c2ecf20Sopenharmony_ci return PTR_ERR(pctrl->clk); 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pctrl->clk); 9408c2ecf20Sopenharmony_ci if (ret) { 9418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "clk enable failed\n"); 9428c2ecf20Sopenharmony_ci return ret; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci raw_spin_lock_init(&pctrl->lock); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci owl_pinctrl_desc.name = dev_name(&pdev->dev); 9488c2ecf20Sopenharmony_ci owl_pinctrl_desc.pins = soc_data->pins; 9498c2ecf20Sopenharmony_ci owl_pinctrl_desc.npins = soc_data->npins; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci pctrl->chip.direction_input = owl_gpio_direction_input; 9528c2ecf20Sopenharmony_ci pctrl->chip.direction_output = owl_gpio_direction_output; 9538c2ecf20Sopenharmony_ci pctrl->chip.get = owl_gpio_get; 9548c2ecf20Sopenharmony_ci pctrl->chip.set = owl_gpio_set; 9558c2ecf20Sopenharmony_ci pctrl->chip.request = owl_gpio_request; 9568c2ecf20Sopenharmony_ci pctrl->chip.free = owl_gpio_free; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci pctrl->soc = soc_data; 9598c2ecf20Sopenharmony_ci pctrl->dev = &pdev->dev; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci pctrl->pctrldev = devm_pinctrl_register(&pdev->dev, 9628c2ecf20Sopenharmony_ci &owl_pinctrl_desc, pctrl); 9638c2ecf20Sopenharmony_ci if (IS_ERR(pctrl->pctrldev)) { 9648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register Actions OWL pinmux driver\n"); 9658c2ecf20Sopenharmony_ci ret = PTR_ERR(pctrl->pctrldev); 9668c2ecf20Sopenharmony_ci goto err_exit; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci ret = platform_irq_count(pdev); 9708c2ecf20Sopenharmony_ci if (ret < 0) 9718c2ecf20Sopenharmony_ci goto err_exit; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci pctrl->num_irq = ret; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci pctrl->irq = devm_kcalloc(&pdev->dev, pctrl->num_irq, 9768c2ecf20Sopenharmony_ci sizeof(*pctrl->irq), GFP_KERNEL); 9778c2ecf20Sopenharmony_ci if (!pctrl->irq) { 9788c2ecf20Sopenharmony_ci ret = -ENOMEM; 9798c2ecf20Sopenharmony_ci goto err_exit; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci for (i = 0; i < pctrl->num_irq ; i++) { 9838c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, i); 9848c2ecf20Sopenharmony_ci if (ret < 0) 9858c2ecf20Sopenharmony_ci goto err_exit; 9868c2ecf20Sopenharmony_ci pctrl->irq[i] = ret; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci ret = owl_gpio_init(pctrl); 9908c2ecf20Sopenharmony_ci if (ret) 9918c2ecf20Sopenharmony_ci goto err_exit; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pctrl); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci return 0; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cierr_exit: 9988c2ecf20Sopenharmony_ci clk_disable_unprepare(pctrl->clk); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci return ret; 10018c2ecf20Sopenharmony_ci} 1002