18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Pin Control and GPIO driver for SuperH Pin Function Controller. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Magnus Damm, Paul Mundt, Laurent Pinchart 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Magnus Damm 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 - 2012 Paul Mundt 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define DRV_NAME "sh-pfc" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/bitops.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/ioport.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pinctrl/machine.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/psci.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/sys_soc.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "core.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int sh_pfc_map_resources(struct sh_pfc *pfc, 318c2ecf20Sopenharmony_ci struct platform_device *pdev) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct sh_pfc_window *windows; 348c2ecf20Sopenharmony_ci unsigned int *irqs = NULL; 358c2ecf20Sopenharmony_ci unsigned int num_windows; 368c2ecf20Sopenharmony_ci struct resource *res; 378c2ecf20Sopenharmony_ci unsigned int i; 388c2ecf20Sopenharmony_ci int num_irqs; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* Count the MEM and IRQ resources. */ 418c2ecf20Sopenharmony_ci for (num_windows = 0;; num_windows++) { 428c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, num_windows); 438c2ecf20Sopenharmony_ci if (!res) 448c2ecf20Sopenharmony_ci break; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci if (num_windows == 0) 478c2ecf20Sopenharmony_ci return -EINVAL; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci num_irqs = platform_irq_count(pdev); 508c2ecf20Sopenharmony_ci if (num_irqs < 0) 518c2ecf20Sopenharmony_ci return num_irqs; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Allocate memory windows and IRQs arrays. */ 548c2ecf20Sopenharmony_ci windows = devm_kcalloc(pfc->dev, num_windows, sizeof(*windows), 558c2ecf20Sopenharmony_ci GFP_KERNEL); 568c2ecf20Sopenharmony_ci if (windows == NULL) 578c2ecf20Sopenharmony_ci return -ENOMEM; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci pfc->num_windows = num_windows; 608c2ecf20Sopenharmony_ci pfc->windows = windows; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (num_irqs) { 638c2ecf20Sopenharmony_ci irqs = devm_kcalloc(pfc->dev, num_irqs, sizeof(*irqs), 648c2ecf20Sopenharmony_ci GFP_KERNEL); 658c2ecf20Sopenharmony_ci if (irqs == NULL) 668c2ecf20Sopenharmony_ci return -ENOMEM; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci pfc->num_irqs = num_irqs; 698c2ecf20Sopenharmony_ci pfc->irqs = irqs; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Fill them. */ 738c2ecf20Sopenharmony_ci for (i = 0; i < num_windows; i++) { 748c2ecf20Sopenharmony_ci windows->virt = devm_platform_get_and_ioremap_resource(pdev, i, &res); 758c2ecf20Sopenharmony_ci if (IS_ERR(windows->virt)) 768c2ecf20Sopenharmony_ci return -ENOMEM; 778c2ecf20Sopenharmony_ci windows->phys = res->start; 788c2ecf20Sopenharmony_ci windows->size = resource_size(res); 798c2ecf20Sopenharmony_ci windows++; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci for (i = 0; i < num_irqs; i++) 828c2ecf20Sopenharmony_ci *irqs++ = platform_get_irq(pdev, i); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, u32 reg) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct sh_pfc_window *window; 908c2ecf20Sopenharmony_ci phys_addr_t address = reg; 918c2ecf20Sopenharmony_ci unsigned int i; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* scan through physical windows and convert address */ 948c2ecf20Sopenharmony_ci for (i = 0; i < pfc->num_windows; i++) { 958c2ecf20Sopenharmony_ci window = pfc->windows + i; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (address < window->phys) 988c2ecf20Sopenharmony_ci continue; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (address >= (window->phys + window->size)) 1018c2ecf20Sopenharmony_ci continue; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return window->virt + (address - window->phys); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci BUG(); 1078c2ecf20Sopenharmony_ci return NULL; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciint sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci unsigned int offset; 1138c2ecf20Sopenharmony_ci unsigned int i; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0, offset = 0; i < pfc->nr_ranges; ++i) { 1168c2ecf20Sopenharmony_ci const struct sh_pfc_pin_range *range = &pfc->ranges[i]; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (pin <= range->end) 1198c2ecf20Sopenharmony_ci return pin >= range->start 1208c2ecf20Sopenharmony_ci ? offset + pin - range->start : -1; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci offset += range->end - range->start + 1; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int sh_pfc_enum_in_range(u16 enum_id, const struct pinmux_range *r) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (enum_id < r->begin) 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (enum_id > r->end) 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 1; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciu32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci switch (reg_width) { 1428c2ecf20Sopenharmony_ci case 8: 1438c2ecf20Sopenharmony_ci return ioread8(mapped_reg); 1448c2ecf20Sopenharmony_ci case 16: 1458c2ecf20Sopenharmony_ci return ioread16(mapped_reg); 1468c2ecf20Sopenharmony_ci case 32: 1478c2ecf20Sopenharmony_ci return ioread32(mapped_reg); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci BUG(); 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_civoid sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width, 1558c2ecf20Sopenharmony_ci u32 data) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci switch (reg_width) { 1588c2ecf20Sopenharmony_ci case 8: 1598c2ecf20Sopenharmony_ci iowrite8(data, mapped_reg); 1608c2ecf20Sopenharmony_ci return; 1618c2ecf20Sopenharmony_ci case 16: 1628c2ecf20Sopenharmony_ci iowrite16(data, mapped_reg); 1638c2ecf20Sopenharmony_ci return; 1648c2ecf20Sopenharmony_ci case 32: 1658c2ecf20Sopenharmony_ci iowrite32(data, mapped_reg); 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci BUG(); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciu32 sh_pfc_read(struct sh_pfc *pfc, u32 reg) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci return sh_pfc_read_raw_reg(sh_pfc_phys_to_virt(pfc, reg), 32); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_civoid sh_pfc_write(struct sh_pfc *pfc, u32 reg, u32 data) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (pfc->info->unlock_reg) 1808c2ecf20Sopenharmony_ci sh_pfc_write_raw_reg( 1818c2ecf20Sopenharmony_ci sh_pfc_phys_to_virt(pfc, pfc->info->unlock_reg), 32, 1828c2ecf20Sopenharmony_ci ~data); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci sh_pfc_write_raw_reg(sh_pfc_phys_to_virt(pfc, reg), 32, data); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void sh_pfc_config_reg_helper(struct sh_pfc *pfc, 1888c2ecf20Sopenharmony_ci const struct pinmux_cfg_reg *crp, 1898c2ecf20Sopenharmony_ci unsigned int in_pos, 1908c2ecf20Sopenharmony_ci void __iomem **mapped_regp, u32 *maskp, 1918c2ecf20Sopenharmony_ci unsigned int *posp) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci unsigned int k; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci *mapped_regp = sh_pfc_phys_to_virt(pfc, crp->reg); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (crp->field_width) { 1988c2ecf20Sopenharmony_ci *maskp = (1 << crp->field_width) - 1; 1998c2ecf20Sopenharmony_ci *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); 2008c2ecf20Sopenharmony_ci } else { 2018c2ecf20Sopenharmony_ci *maskp = (1 << crp->var_field_width[in_pos]) - 1; 2028c2ecf20Sopenharmony_ci *posp = crp->reg_width; 2038c2ecf20Sopenharmony_ci for (k = 0; k <= in_pos; k++) 2048c2ecf20Sopenharmony_ci *posp -= crp->var_field_width[k]; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void sh_pfc_write_config_reg(struct sh_pfc *pfc, 2098c2ecf20Sopenharmony_ci const struct pinmux_cfg_reg *crp, 2108c2ecf20Sopenharmony_ci unsigned int field, u32 value) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci void __iomem *mapped_reg; 2138c2ecf20Sopenharmony_ci unsigned int pos; 2148c2ecf20Sopenharmony_ci u32 mask, data; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci sh_pfc_config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci dev_dbg(pfc->dev, "write_reg addr = %x, value = 0x%x, field = %u, " 2198c2ecf20Sopenharmony_ci "r_width = %u, f_width = %u\n", 2208c2ecf20Sopenharmony_ci crp->reg, value, field, crp->reg_width, hweight32(mask)); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci mask = ~(mask << pos); 2238c2ecf20Sopenharmony_ci value = value << pos; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci data = sh_pfc_read_raw_reg(mapped_reg, crp->reg_width); 2268c2ecf20Sopenharmony_ci data &= mask; 2278c2ecf20Sopenharmony_ci data |= value; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (pfc->info->unlock_reg) 2308c2ecf20Sopenharmony_ci sh_pfc_write_raw_reg( 2318c2ecf20Sopenharmony_ci sh_pfc_phys_to_virt(pfc, pfc->info->unlock_reg), 32, 2328c2ecf20Sopenharmony_ci ~data); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci sh_pfc_write_raw_reg(mapped_reg, crp->reg_width, data); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int sh_pfc_get_config_reg(struct sh_pfc *pfc, u16 enum_id, 2388c2ecf20Sopenharmony_ci const struct pinmux_cfg_reg **crp, 2398c2ecf20Sopenharmony_ci unsigned int *fieldp, u32 *valuep) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci unsigned int k = 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci while (1) { 2448c2ecf20Sopenharmony_ci const struct pinmux_cfg_reg *config_reg = 2458c2ecf20Sopenharmony_ci pfc->info->cfg_regs + k; 2468c2ecf20Sopenharmony_ci unsigned int r_width = config_reg->reg_width; 2478c2ecf20Sopenharmony_ci unsigned int f_width = config_reg->field_width; 2488c2ecf20Sopenharmony_ci unsigned int curr_width; 2498c2ecf20Sopenharmony_ci unsigned int bit_pos; 2508c2ecf20Sopenharmony_ci unsigned int pos = 0; 2518c2ecf20Sopenharmony_ci unsigned int m = 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!r_width) 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { 2578c2ecf20Sopenharmony_ci u32 ncomb; 2588c2ecf20Sopenharmony_ci u32 n; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (f_width) 2618c2ecf20Sopenharmony_ci curr_width = f_width; 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci curr_width = config_reg->var_field_width[m]; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ncomb = 1 << curr_width; 2668c2ecf20Sopenharmony_ci for (n = 0; n < ncomb; n++) { 2678c2ecf20Sopenharmony_ci if (config_reg->enum_ids[pos + n] == enum_id) { 2688c2ecf20Sopenharmony_ci *crp = config_reg; 2698c2ecf20Sopenharmony_ci *fieldp = m; 2708c2ecf20Sopenharmony_ci *valuep = n; 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci pos += ncomb; 2758c2ecf20Sopenharmony_ci m++; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci k++; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int sh_pfc_mark_to_enum(struct sh_pfc *pfc, u16 mark, int pos, 2848c2ecf20Sopenharmony_ci u16 *enum_idp) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci const u16 *data = pfc->info->pinmux_data; 2878c2ecf20Sopenharmony_ci unsigned int k; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (pos) { 2908c2ecf20Sopenharmony_ci *enum_idp = data[pos + 1]; 2918c2ecf20Sopenharmony_ci return pos + 1; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci for (k = 0; k < pfc->info->pinmux_data_size; k++) { 2958c2ecf20Sopenharmony_ci if (data[k] == mark) { 2968c2ecf20Sopenharmony_ci *enum_idp = data[k + 1]; 2978c2ecf20Sopenharmony_ci return k + 1; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci dev_err(pfc->dev, "cannot locate data/mark enum_id for mark %d\n", 3028c2ecf20Sopenharmony_ci mark); 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciint sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci const struct pinmux_range *range; 3098c2ecf20Sopenharmony_ci int pos = 0; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci switch (pinmux_type) { 3128c2ecf20Sopenharmony_ci case PINMUX_TYPE_GPIO: 3138c2ecf20Sopenharmony_ci case PINMUX_TYPE_FUNCTION: 3148c2ecf20Sopenharmony_ci range = NULL; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci case PINMUX_TYPE_OUTPUT: 3188c2ecf20Sopenharmony_ci range = &pfc->info->output; 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci case PINMUX_TYPE_INPUT: 3228c2ecf20Sopenharmony_ci range = &pfc->info->input; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci default: 3268c2ecf20Sopenharmony_ci return -EINVAL; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Iterate over all the configuration fields we need to update. */ 3308c2ecf20Sopenharmony_ci while (1) { 3318c2ecf20Sopenharmony_ci const struct pinmux_cfg_reg *cr; 3328c2ecf20Sopenharmony_ci unsigned int field; 3338c2ecf20Sopenharmony_ci u16 enum_id; 3348c2ecf20Sopenharmony_ci u32 value; 3358c2ecf20Sopenharmony_ci int in_range; 3368c2ecf20Sopenharmony_ci int ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci pos = sh_pfc_mark_to_enum(pfc, mark, pos, &enum_id); 3398c2ecf20Sopenharmony_ci if (pos < 0) 3408c2ecf20Sopenharmony_ci return pos; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!enum_id) 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Check if the configuration field selects a function. If it 3468c2ecf20Sopenharmony_ci * doesn't, skip the field if it's not applicable to the 3478c2ecf20Sopenharmony_ci * requested pinmux type. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci in_range = sh_pfc_enum_in_range(enum_id, &pfc->info->function); 3508c2ecf20Sopenharmony_ci if (!in_range) { 3518c2ecf20Sopenharmony_ci if (pinmux_type == PINMUX_TYPE_FUNCTION) { 3528c2ecf20Sopenharmony_ci /* Functions are allowed to modify all 3538c2ecf20Sopenharmony_ci * fields. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci in_range = 1; 3568c2ecf20Sopenharmony_ci } else if (pinmux_type != PINMUX_TYPE_GPIO) { 3578c2ecf20Sopenharmony_ci /* Input/output types can only modify fields 3588c2ecf20Sopenharmony_ci * that correspond to their respective ranges. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci in_range = sh_pfc_enum_in_range(enum_id, range); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * special case pass through for fixed 3648c2ecf20Sopenharmony_ci * input-only or output-only pins without 3658c2ecf20Sopenharmony_ci * function enum register association. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci if (in_range && enum_id == range->force) 3688c2ecf20Sopenharmony_ci continue; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci /* GPIOs are only allowed to modify function fields. */ 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (!in_range) 3748c2ecf20Sopenharmony_ci continue; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ret = sh_pfc_get_config_reg(pfc, enum_id, &cr, &field, &value); 3778c2ecf20Sopenharmony_ci if (ret < 0) 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci sh_pfc_write_config_reg(pfc, cr, field, value); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciconst struct pinmux_bias_reg * 3878c2ecf20Sopenharmony_cish_pfc_pin_to_bias_reg(const struct sh_pfc *pfc, unsigned int pin, 3888c2ecf20Sopenharmony_ci unsigned int *bit) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci unsigned int i, j; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci for (i = 0; pfc->info->bias_regs[i].puen; i++) { 3938c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(pfc->info->bias_regs[i].pins); j++) { 3948c2ecf20Sopenharmony_ci if (pfc->info->bias_regs[i].pins[j] == pin) { 3958c2ecf20Sopenharmony_ci *bit = j; 3968c2ecf20Sopenharmony_ci return &pfc->info->bias_regs[i]; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci WARN_ONCE(1, "Pin %u is not in bias info list\n", pin); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return NULL; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int sh_pfc_init_ranges(struct sh_pfc *pfc) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct sh_pfc_pin_range *range; 4098c2ecf20Sopenharmony_ci unsigned int nr_ranges; 4108c2ecf20Sopenharmony_ci unsigned int i; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (pfc->info->pins[0].pin == (u16)-1) { 4138c2ecf20Sopenharmony_ci /* Pin number -1 denotes that the SoC doesn't report pin numbers 4148c2ecf20Sopenharmony_ci * in its pin arrays yet. Consider the pin numbers range as 4158c2ecf20Sopenharmony_ci * continuous and allocate a single range. 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci pfc->nr_ranges = 1; 4188c2ecf20Sopenharmony_ci pfc->ranges = devm_kzalloc(pfc->dev, sizeof(*pfc->ranges), 4198c2ecf20Sopenharmony_ci GFP_KERNEL); 4208c2ecf20Sopenharmony_ci if (pfc->ranges == NULL) 4218c2ecf20Sopenharmony_ci return -ENOMEM; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci pfc->ranges->start = 0; 4248c2ecf20Sopenharmony_ci pfc->ranges->end = pfc->info->nr_pins - 1; 4258c2ecf20Sopenharmony_ci pfc->nr_gpio_pins = pfc->info->nr_pins; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* Count, allocate and fill the ranges. The PFC SoC data pins array must 4318c2ecf20Sopenharmony_ci * be sorted by pin numbers, and pins without a GPIO port must come 4328c2ecf20Sopenharmony_ci * last. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci for (i = 1, nr_ranges = 1; i < pfc->info->nr_pins; ++i) { 4358c2ecf20Sopenharmony_ci if (pfc->info->pins[i-1].pin != pfc->info->pins[i].pin - 1) 4368c2ecf20Sopenharmony_ci nr_ranges++; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci pfc->nr_ranges = nr_ranges; 4408c2ecf20Sopenharmony_ci pfc->ranges = devm_kcalloc(pfc->dev, nr_ranges, sizeof(*pfc->ranges), 4418c2ecf20Sopenharmony_ci GFP_KERNEL); 4428c2ecf20Sopenharmony_ci if (pfc->ranges == NULL) 4438c2ecf20Sopenharmony_ci return -ENOMEM; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci range = pfc->ranges; 4468c2ecf20Sopenharmony_ci range->start = pfc->info->pins[0].pin; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci for (i = 1; i < pfc->info->nr_pins; ++i) { 4498c2ecf20Sopenharmony_ci if (pfc->info->pins[i-1].pin == pfc->info->pins[i].pin - 1) 4508c2ecf20Sopenharmony_ci continue; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci range->end = pfc->info->pins[i-1].pin; 4538c2ecf20Sopenharmony_ci if (!(pfc->info->pins[i-1].configs & SH_PFC_PIN_CFG_NO_GPIO)) 4548c2ecf20Sopenharmony_ci pfc->nr_gpio_pins = range->end + 1; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci range++; 4578c2ecf20Sopenharmony_ci range->start = pfc->info->pins[i].pin; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci range->end = pfc->info->pins[i-1].pin; 4618c2ecf20Sopenharmony_ci if (!(pfc->info->pins[i-1].configs & SH_PFC_PIN_CFG_NO_GPIO)) 4628c2ecf20Sopenharmony_ci pfc->nr_gpio_pins = range->end + 1; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 4688c2ecf20Sopenharmony_cistatic const struct of_device_id sh_pfc_of_table[] = { 4698c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_EMEV2 4708c2ecf20Sopenharmony_ci { 4718c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-emev2", 4728c2ecf20Sopenharmony_ci .data = &emev2_pinmux_info, 4738c2ecf20Sopenharmony_ci }, 4748c2ecf20Sopenharmony_ci#endif 4758c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A73A4 4768c2ecf20Sopenharmony_ci { 4778c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a73a4", 4788c2ecf20Sopenharmony_ci .data = &r8a73a4_pinmux_info, 4798c2ecf20Sopenharmony_ci }, 4808c2ecf20Sopenharmony_ci#endif 4818c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7740 4828c2ecf20Sopenharmony_ci { 4838c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7740", 4848c2ecf20Sopenharmony_ci .data = &r8a7740_pinmux_info, 4858c2ecf20Sopenharmony_ci }, 4868c2ecf20Sopenharmony_ci#endif 4878c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7742 4888c2ecf20Sopenharmony_ci { 4898c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7742", 4908c2ecf20Sopenharmony_ci .data = &r8a7742_pinmux_info, 4918c2ecf20Sopenharmony_ci }, 4928c2ecf20Sopenharmony_ci#endif 4938c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7743 4948c2ecf20Sopenharmony_ci { 4958c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7743", 4968c2ecf20Sopenharmony_ci .data = &r8a7743_pinmux_info, 4978c2ecf20Sopenharmony_ci }, 4988c2ecf20Sopenharmony_ci#endif 4998c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7744 5008c2ecf20Sopenharmony_ci { 5018c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7744", 5028c2ecf20Sopenharmony_ci .data = &r8a7744_pinmux_info, 5038c2ecf20Sopenharmony_ci }, 5048c2ecf20Sopenharmony_ci#endif 5058c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7745 5068c2ecf20Sopenharmony_ci { 5078c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7745", 5088c2ecf20Sopenharmony_ci .data = &r8a7745_pinmux_info, 5098c2ecf20Sopenharmony_ci }, 5108c2ecf20Sopenharmony_ci#endif 5118c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77470 5128c2ecf20Sopenharmony_ci { 5138c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a77470", 5148c2ecf20Sopenharmony_ci .data = &r8a77470_pinmux_info, 5158c2ecf20Sopenharmony_ci }, 5168c2ecf20Sopenharmony_ci#endif 5178c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774A1 5188c2ecf20Sopenharmony_ci { 5198c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a774a1", 5208c2ecf20Sopenharmony_ci .data = &r8a774a1_pinmux_info, 5218c2ecf20Sopenharmony_ci }, 5228c2ecf20Sopenharmony_ci#endif 5238c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774B1 5248c2ecf20Sopenharmony_ci { 5258c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a774b1", 5268c2ecf20Sopenharmony_ci .data = &r8a774b1_pinmux_info, 5278c2ecf20Sopenharmony_ci }, 5288c2ecf20Sopenharmony_ci#endif 5298c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774C0 5308c2ecf20Sopenharmony_ci { 5318c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a774c0", 5328c2ecf20Sopenharmony_ci .data = &r8a774c0_pinmux_info, 5338c2ecf20Sopenharmony_ci }, 5348c2ecf20Sopenharmony_ci#endif 5358c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774E1 5368c2ecf20Sopenharmony_ci { 5378c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a774e1", 5388c2ecf20Sopenharmony_ci .data = &r8a774e1_pinmux_info, 5398c2ecf20Sopenharmony_ci }, 5408c2ecf20Sopenharmony_ci#endif 5418c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7778 5428c2ecf20Sopenharmony_ci { 5438c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7778", 5448c2ecf20Sopenharmony_ci .data = &r8a7778_pinmux_info, 5458c2ecf20Sopenharmony_ci }, 5468c2ecf20Sopenharmony_ci#endif 5478c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7779 5488c2ecf20Sopenharmony_ci { 5498c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7779", 5508c2ecf20Sopenharmony_ci .data = &r8a7779_pinmux_info, 5518c2ecf20Sopenharmony_ci }, 5528c2ecf20Sopenharmony_ci#endif 5538c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7790 5548c2ecf20Sopenharmony_ci { 5558c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7790", 5568c2ecf20Sopenharmony_ci .data = &r8a7790_pinmux_info, 5578c2ecf20Sopenharmony_ci }, 5588c2ecf20Sopenharmony_ci#endif 5598c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7791 5608c2ecf20Sopenharmony_ci { 5618c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7791", 5628c2ecf20Sopenharmony_ci .data = &r8a7791_pinmux_info, 5638c2ecf20Sopenharmony_ci }, 5648c2ecf20Sopenharmony_ci#endif 5658c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7792 5668c2ecf20Sopenharmony_ci { 5678c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7792", 5688c2ecf20Sopenharmony_ci .data = &r8a7792_pinmux_info, 5698c2ecf20Sopenharmony_ci }, 5708c2ecf20Sopenharmony_ci#endif 5718c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7793 5728c2ecf20Sopenharmony_ci { 5738c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7793", 5748c2ecf20Sopenharmony_ci .data = &r8a7793_pinmux_info, 5758c2ecf20Sopenharmony_ci }, 5768c2ecf20Sopenharmony_ci#endif 5778c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7794 5788c2ecf20Sopenharmony_ci { 5798c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7794", 5808c2ecf20Sopenharmony_ci .data = &r8a7794_pinmux_info, 5818c2ecf20Sopenharmony_ci }, 5828c2ecf20Sopenharmony_ci#endif 5838c2ecf20Sopenharmony_ci/* Both r8a7795 entries must be present to make sanity checks work */ 5848c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77950 5858c2ecf20Sopenharmony_ci { 5868c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7795", 5878c2ecf20Sopenharmony_ci .data = &r8a77950_pinmux_info, 5888c2ecf20Sopenharmony_ci }, 5898c2ecf20Sopenharmony_ci#endif 5908c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77951 5918c2ecf20Sopenharmony_ci { 5928c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7795", 5938c2ecf20Sopenharmony_ci .data = &r8a77951_pinmux_info, 5948c2ecf20Sopenharmony_ci }, 5958c2ecf20Sopenharmony_ci#endif 5968c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77960 5978c2ecf20Sopenharmony_ci { 5988c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a7796", 5998c2ecf20Sopenharmony_ci .data = &r8a77960_pinmux_info, 6008c2ecf20Sopenharmony_ci }, 6018c2ecf20Sopenharmony_ci#endif 6028c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77961 6038c2ecf20Sopenharmony_ci { 6048c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a77961", 6058c2ecf20Sopenharmony_ci .data = &r8a77961_pinmux_info, 6068c2ecf20Sopenharmony_ci }, 6078c2ecf20Sopenharmony_ci#endif 6088c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77965 6098c2ecf20Sopenharmony_ci { 6108c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a77965", 6118c2ecf20Sopenharmony_ci .data = &r8a77965_pinmux_info, 6128c2ecf20Sopenharmony_ci }, 6138c2ecf20Sopenharmony_ci#endif 6148c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77970 6158c2ecf20Sopenharmony_ci { 6168c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a77970", 6178c2ecf20Sopenharmony_ci .data = &r8a77970_pinmux_info, 6188c2ecf20Sopenharmony_ci }, 6198c2ecf20Sopenharmony_ci#endif 6208c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77980 6218c2ecf20Sopenharmony_ci { 6228c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a77980", 6238c2ecf20Sopenharmony_ci .data = &r8a77980_pinmux_info, 6248c2ecf20Sopenharmony_ci }, 6258c2ecf20Sopenharmony_ci#endif 6268c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77990 6278c2ecf20Sopenharmony_ci { 6288c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a77990", 6298c2ecf20Sopenharmony_ci .data = &r8a77990_pinmux_info, 6308c2ecf20Sopenharmony_ci }, 6318c2ecf20Sopenharmony_ci#endif 6328c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77995 6338c2ecf20Sopenharmony_ci { 6348c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-r8a77995", 6358c2ecf20Sopenharmony_ci .data = &r8a77995_pinmux_info, 6368c2ecf20Sopenharmony_ci }, 6378c2ecf20Sopenharmony_ci#endif 6388c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH73A0 6398c2ecf20Sopenharmony_ci { 6408c2ecf20Sopenharmony_ci .compatible = "renesas,pfc-sh73a0", 6418c2ecf20Sopenharmony_ci .data = &sh73a0_pinmux_info, 6428c2ecf20Sopenharmony_ci }, 6438c2ecf20Sopenharmony_ci#endif 6448c2ecf20Sopenharmony_ci { }, 6458c2ecf20Sopenharmony_ci}; 6468c2ecf20Sopenharmony_ci#endif 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW) 6498c2ecf20Sopenharmony_cistatic void sh_pfc_nop_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic void sh_pfc_save_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci pfc->saved_regs[idx] = sh_pfc_read(pfc, reg); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic void sh_pfc_restore_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci sh_pfc_write(pfc, reg, pfc->saved_regs[idx]); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic unsigned int sh_pfc_walk_regs(struct sh_pfc *pfc, 6648c2ecf20Sopenharmony_ci void (*do_reg)(struct sh_pfc *pfc, u32 reg, unsigned int idx)) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci unsigned int i, n = 0; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (pfc->info->cfg_regs) 6698c2ecf20Sopenharmony_ci for (i = 0; pfc->info->cfg_regs[i].reg; i++) 6708c2ecf20Sopenharmony_ci do_reg(pfc, pfc->info->cfg_regs[i].reg, n++); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (pfc->info->drive_regs) 6738c2ecf20Sopenharmony_ci for (i = 0; pfc->info->drive_regs[i].reg; i++) 6748c2ecf20Sopenharmony_ci do_reg(pfc, pfc->info->drive_regs[i].reg, n++); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (pfc->info->bias_regs) 6778c2ecf20Sopenharmony_ci for (i = 0; pfc->info->bias_regs[i].puen; i++) { 6788c2ecf20Sopenharmony_ci do_reg(pfc, pfc->info->bias_regs[i].puen, n++); 6798c2ecf20Sopenharmony_ci if (pfc->info->bias_regs[i].pud) 6808c2ecf20Sopenharmony_ci do_reg(pfc, pfc->info->bias_regs[i].pud, n++); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (pfc->info->ioctrl_regs) 6848c2ecf20Sopenharmony_ci for (i = 0; pfc->info->ioctrl_regs[i].reg; i++) 6858c2ecf20Sopenharmony_ci do_reg(pfc, pfc->info->ioctrl_regs[i].reg, n++); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return n; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic int sh_pfc_suspend_init(struct sh_pfc *pfc) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci unsigned int n; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* This is the best we can do to check for the presence of PSCI */ 6958c2ecf20Sopenharmony_ci if (!psci_ops.cpu_suspend) 6968c2ecf20Sopenharmony_ci return 0; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci n = sh_pfc_walk_regs(pfc, sh_pfc_nop_reg); 6998c2ecf20Sopenharmony_ci if (!n) 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci pfc->saved_regs = devm_kmalloc_array(pfc->dev, n, 7038c2ecf20Sopenharmony_ci sizeof(*pfc->saved_regs), 7048c2ecf20Sopenharmony_ci GFP_KERNEL); 7058c2ecf20Sopenharmony_ci if (!pfc->saved_regs) 7068c2ecf20Sopenharmony_ci return -ENOMEM; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci dev_dbg(pfc->dev, "Allocated space to save %u regs\n", n); 7098c2ecf20Sopenharmony_ci return 0; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int sh_pfc_suspend_noirq(struct device *dev) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct sh_pfc *pfc = dev_get_drvdata(dev); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (pfc->saved_regs) 7178c2ecf20Sopenharmony_ci sh_pfc_walk_regs(pfc, sh_pfc_save_reg); 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic int sh_pfc_resume_noirq(struct device *dev) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct sh_pfc *pfc = dev_get_drvdata(dev); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (pfc->saved_regs) 7268c2ecf20Sopenharmony_ci sh_pfc_walk_regs(pfc, sh_pfc_restore_reg); 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sh_pfc_pm = { 7318c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sh_pfc_suspend_noirq, sh_pfc_resume_noirq) 7328c2ecf20Sopenharmony_ci}; 7338c2ecf20Sopenharmony_ci#define DEV_PM_OPS &sh_pfc_pm 7348c2ecf20Sopenharmony_ci#else 7358c2ecf20Sopenharmony_cistatic int sh_pfc_suspend_init(struct sh_pfc *pfc) { return 0; } 7368c2ecf20Sopenharmony_ci#define DEV_PM_OPS NULL 7378c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */ 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci#ifdef DEBUG 7408c2ecf20Sopenharmony_ci#define SH_PFC_MAX_REGS 300 7418c2ecf20Sopenharmony_ci#define SH_PFC_MAX_ENUMS 5000 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic unsigned int sh_pfc_errors __initdata = 0; 7448c2ecf20Sopenharmony_cistatic unsigned int sh_pfc_warnings __initdata = 0; 7458c2ecf20Sopenharmony_cistatic u32 *sh_pfc_regs __initdata = NULL; 7468c2ecf20Sopenharmony_cistatic u32 sh_pfc_num_regs __initdata = 0; 7478c2ecf20Sopenharmony_cistatic u16 *sh_pfc_enums __initdata = NULL; 7488c2ecf20Sopenharmony_cistatic u32 sh_pfc_num_enums __initdata = 0; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci#define sh_pfc_err(fmt, ...) \ 7518c2ecf20Sopenharmony_ci do { \ 7528c2ecf20Sopenharmony_ci pr_err("%s: " fmt, drvname, ##__VA_ARGS__); \ 7538c2ecf20Sopenharmony_ci sh_pfc_errors++; \ 7548c2ecf20Sopenharmony_ci } while (0) 7558c2ecf20Sopenharmony_ci#define sh_pfc_warn(fmt, ...) \ 7568c2ecf20Sopenharmony_ci do { \ 7578c2ecf20Sopenharmony_ci pr_warn("%s: " fmt, drvname, ##__VA_ARGS__); \ 7588c2ecf20Sopenharmony_ci sh_pfc_warnings++; \ 7598c2ecf20Sopenharmony_ci } while (0) 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic bool __init is0s(const u16 *enum_ids, unsigned int n) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci unsigned int i; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 7668c2ecf20Sopenharmony_ci if (enum_ids[i]) 7678c2ecf20Sopenharmony_ci return false; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci return true; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic bool __init same_name(const char *a, const char *b) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci if (!a || !b) 7758c2ecf20Sopenharmony_ci return false; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return !strcmp(a, b); 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_reg(const char *drvname, u32 reg) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci unsigned int i; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci for (i = 0; i < sh_pfc_num_regs; i++) 7858c2ecf20Sopenharmony_ci if (reg == sh_pfc_regs[i]) { 7868c2ecf20Sopenharmony_ci sh_pfc_err("reg 0x%x conflict\n", reg); 7878c2ecf20Sopenharmony_ci return; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (sh_pfc_num_regs == SH_PFC_MAX_REGS) { 7918c2ecf20Sopenharmony_ci pr_warn_once("%s: Please increase SH_PFC_MAX_REGS\n", drvname); 7928c2ecf20Sopenharmony_ci return; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci sh_pfc_regs[sh_pfc_num_regs++] = reg; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic int __init sh_pfc_check_enum(const char *drvname, u16 enum_id) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci unsigned int i; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (i = 0; i < sh_pfc_num_enums; i++) { 8038c2ecf20Sopenharmony_ci if (enum_id == sh_pfc_enums[i]) 8048c2ecf20Sopenharmony_ci return -EINVAL; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (sh_pfc_num_enums == SH_PFC_MAX_ENUMS) { 8088c2ecf20Sopenharmony_ci pr_warn_once("%s: Please increase SH_PFC_MAX_ENUMS\n", drvname); 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci sh_pfc_enums[sh_pfc_num_enums++] = enum_id; 8138c2ecf20Sopenharmony_ci return 0; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_reg_enums(const char *drvname, u32 reg, 8178c2ecf20Sopenharmony_ci const u16 *enums, unsigned int n) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci unsigned int i; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 8228c2ecf20Sopenharmony_ci if (enums[i] && sh_pfc_check_enum(drvname, enums[i])) 8238c2ecf20Sopenharmony_ci sh_pfc_err("reg 0x%x enum_id %u conflict\n", reg, 8248c2ecf20Sopenharmony_ci enums[i]); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_pin(const struct sh_pfc_soc_info *info, 8298c2ecf20Sopenharmony_ci u32 reg, unsigned int pin) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci const char *drvname = info->name; 8328c2ecf20Sopenharmony_ci unsigned int i; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (pin == SH_PFC_PIN_NONE) 8358c2ecf20Sopenharmony_ci return; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_pins; i++) { 8388c2ecf20Sopenharmony_ci if (pin == info->pins[i].pin) 8398c2ecf20Sopenharmony_ci return; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci sh_pfc_err("reg 0x%x: pin %u not found\n", reg, pin); 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_cfg_reg(const char *drvname, 8468c2ecf20Sopenharmony_ci const struct pinmux_cfg_reg *cfg_reg) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci unsigned int i, n, rw, fw; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci sh_pfc_check_reg(drvname, cfg_reg->reg); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (cfg_reg->field_width) { 8538c2ecf20Sopenharmony_ci fw = cfg_reg->field_width; 8548c2ecf20Sopenharmony_ci n = (cfg_reg->reg_width / fw) << fw; 8558c2ecf20Sopenharmony_ci /* Skip field checks (done at build time) */ 8568c2ecf20Sopenharmony_ci goto check_enum_ids; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci for (i = 0, n = 0, rw = 0; (fw = cfg_reg->var_field_width[i]); i++) { 8608c2ecf20Sopenharmony_ci if (fw > 3 && is0s(&cfg_reg->enum_ids[n], 1 << fw)) 8618c2ecf20Sopenharmony_ci sh_pfc_warn("reg 0x%x: reserved field [%u:%u] can be split to reduce table size\n", 8628c2ecf20Sopenharmony_ci cfg_reg->reg, rw, rw + fw - 1); 8638c2ecf20Sopenharmony_ci n += 1 << fw; 8648c2ecf20Sopenharmony_ci rw += fw; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (rw != cfg_reg->reg_width) 8688c2ecf20Sopenharmony_ci sh_pfc_err("reg 0x%x: var_field_width declares %u instead of %u bits\n", 8698c2ecf20Sopenharmony_ci cfg_reg->reg, rw, cfg_reg->reg_width); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (n != cfg_reg->nr_enum_ids) 8728c2ecf20Sopenharmony_ci sh_pfc_err("reg 0x%x: enum_ids[] has %u instead of %u values\n", 8738c2ecf20Sopenharmony_ci cfg_reg->reg, cfg_reg->nr_enum_ids, n); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cicheck_enum_ids: 8768c2ecf20Sopenharmony_ci sh_pfc_check_reg_enums(drvname, cfg_reg->reg, cfg_reg->enum_ids, n); 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_drive_reg(const struct sh_pfc_soc_info *info, 8808c2ecf20Sopenharmony_ci const struct pinmux_drive_reg *drive) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci const char *drvname = info->name; 8838c2ecf20Sopenharmony_ci unsigned long seen = 0, mask; 8848c2ecf20Sopenharmony_ci unsigned int i; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci sh_pfc_check_reg(info->name, drive->reg); 8878c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drive->fields); i++) { 8888c2ecf20Sopenharmony_ci const struct pinmux_drive_reg_field *field = &drive->fields[i]; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (!field->pin && !field->offset && !field->size) 8918c2ecf20Sopenharmony_ci continue; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci mask = GENMASK(field->offset + field->size - 1, field->offset); 8948c2ecf20Sopenharmony_ci if (mask & seen) 8958c2ecf20Sopenharmony_ci sh_pfc_err("drive_reg 0x%x: field %u overlap\n", 8968c2ecf20Sopenharmony_ci drive->reg, i); 8978c2ecf20Sopenharmony_ci seen |= mask; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci sh_pfc_check_pin(info, drive->reg, field->pin); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_bias_reg(const struct sh_pfc_soc_info *info, 9048c2ecf20Sopenharmony_ci const struct pinmux_bias_reg *bias) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci unsigned int i; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci sh_pfc_check_reg(info->name, bias->puen); 9098c2ecf20Sopenharmony_ci if (bias->pud) 9108c2ecf20Sopenharmony_ci sh_pfc_check_reg(info->name, bias->pud); 9118c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bias->pins); i++) 9128c2ecf20Sopenharmony_ci sh_pfc_check_pin(info, bias->puen, bias->pins[i]); 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_info(const struct sh_pfc_soc_info *info) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci const char *drvname = info->name; 9188c2ecf20Sopenharmony_ci unsigned int *refcnts; 9198c2ecf20Sopenharmony_ci unsigned int i, j, k; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci pr_info("Checking %s\n", drvname); 9228c2ecf20Sopenharmony_ci sh_pfc_num_regs = 0; 9238c2ecf20Sopenharmony_ci sh_pfc_num_enums = 0; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* Check pins */ 9268c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_pins; i++) { 9278c2ecf20Sopenharmony_ci const struct sh_pfc_pin *pin = &info->pins[i]; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (!pin->name) { 9308c2ecf20Sopenharmony_ci sh_pfc_err("empty pin %u\n", i); 9318c2ecf20Sopenharmony_ci continue; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 9348c2ecf20Sopenharmony_ci const struct sh_pfc_pin *pin2 = &info->pins[j]; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (same_name(pin->name, pin2->name)) 9378c2ecf20Sopenharmony_ci sh_pfc_err("pin %s: name conflict\n", 9388c2ecf20Sopenharmony_ci pin->name); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (pin->pin != (u16)-1 && pin->pin == pin2->pin) 9418c2ecf20Sopenharmony_ci sh_pfc_err("pin %s/%s: pin %u conflict\n", 9428c2ecf20Sopenharmony_ci pin->name, pin2->name, pin->pin); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (pin->enum_id && pin->enum_id == pin2->enum_id) 9458c2ecf20Sopenharmony_ci sh_pfc_err("pin %s/%s: enum_id %u conflict\n", 9468c2ecf20Sopenharmony_ci pin->name, pin2->name, 9478c2ecf20Sopenharmony_ci pin->enum_id); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* Check groups and functions */ 9528c2ecf20Sopenharmony_ci refcnts = kcalloc(info->nr_groups, sizeof(*refcnts), GFP_KERNEL); 9538c2ecf20Sopenharmony_ci if (!refcnts) 9548c2ecf20Sopenharmony_ci return; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_functions; i++) { 9578c2ecf20Sopenharmony_ci const struct sh_pfc_function *func = &info->functions[i]; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (!func->name) { 9608c2ecf20Sopenharmony_ci sh_pfc_err("empty function %u\n", i); 9618c2ecf20Sopenharmony_ci continue; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 9648c2ecf20Sopenharmony_ci if (same_name(func->name, info->functions[j].name)) 9658c2ecf20Sopenharmony_ci sh_pfc_err("function %s: name conflict\n", 9668c2ecf20Sopenharmony_ci func->name); 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci for (j = 0; j < func->nr_groups; j++) { 9698c2ecf20Sopenharmony_ci for (k = 0; k < info->nr_groups; k++) { 9708c2ecf20Sopenharmony_ci if (same_name(func->groups[j], 9718c2ecf20Sopenharmony_ci info->groups[k].name)) { 9728c2ecf20Sopenharmony_ci refcnts[k]++; 9738c2ecf20Sopenharmony_ci break; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (k == info->nr_groups) 9788c2ecf20Sopenharmony_ci sh_pfc_err("function %s: group %s not found\n", 9798c2ecf20Sopenharmony_ci func->name, func->groups[j]); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_groups; i++) { 9848c2ecf20Sopenharmony_ci const struct sh_pfc_pin_group *group = &info->groups[i]; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci if (!group->name) { 9878c2ecf20Sopenharmony_ci sh_pfc_err("empty group %u\n", i); 9888c2ecf20Sopenharmony_ci continue; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 9918c2ecf20Sopenharmony_ci if (same_name(group->name, info->groups[j].name)) 9928c2ecf20Sopenharmony_ci sh_pfc_err("group %s: name conflict\n", 9938c2ecf20Sopenharmony_ci group->name); 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci if (!refcnts[i]) 9968c2ecf20Sopenharmony_ci sh_pfc_err("orphan group %s\n", group->name); 9978c2ecf20Sopenharmony_ci else if (refcnts[i] > 1) 9988c2ecf20Sopenharmony_ci sh_pfc_warn("group %s referenced by %u functions\n", 9998c2ecf20Sopenharmony_ci group->name, refcnts[i]); 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci kfree(refcnts); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* Check config register descriptions */ 10058c2ecf20Sopenharmony_ci for (i = 0; info->cfg_regs && info->cfg_regs[i].reg; i++) 10068c2ecf20Sopenharmony_ci sh_pfc_check_cfg_reg(drvname, &info->cfg_regs[i]); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* Check drive strength registers */ 10098c2ecf20Sopenharmony_ci for (i = 0; info->drive_regs && info->drive_regs[i].reg; i++) 10108c2ecf20Sopenharmony_ci sh_pfc_check_drive_reg(info, &info->drive_regs[i]); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* Check bias registers */ 10138c2ecf20Sopenharmony_ci for (i = 0; info->bias_regs && info->bias_regs[i].puen; i++) 10148c2ecf20Sopenharmony_ci sh_pfc_check_bias_reg(info, &info->bias_regs[i]); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* Check ioctrl registers */ 10178c2ecf20Sopenharmony_ci for (i = 0; info->ioctrl_regs && info->ioctrl_regs[i].reg; i++) 10188c2ecf20Sopenharmony_ci sh_pfc_check_reg(drvname, info->ioctrl_regs[i].reg); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* Check data registers */ 10218c2ecf20Sopenharmony_ci for (i = 0; info->data_regs && info->data_regs[i].reg; i++) { 10228c2ecf20Sopenharmony_ci sh_pfc_check_reg(drvname, info->data_regs[i].reg); 10238c2ecf20Sopenharmony_ci sh_pfc_check_reg_enums(drvname, info->data_regs[i].reg, 10248c2ecf20Sopenharmony_ci info->data_regs[i].enum_ids, 10258c2ecf20Sopenharmony_ci info->data_regs[i].reg_width); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_SH_FUNC_GPIO 10298c2ecf20Sopenharmony_ci /* Check function GPIOs */ 10308c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_func_gpios; i++) { 10318c2ecf20Sopenharmony_ci const struct pinmux_func *func = &info->func_gpios[i]; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (!func->name) { 10348c2ecf20Sopenharmony_ci sh_pfc_err("empty function gpio %u\n", i); 10358c2ecf20Sopenharmony_ci continue; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 10388c2ecf20Sopenharmony_ci if (same_name(func->name, info->func_gpios[j].name)) 10398c2ecf20Sopenharmony_ci sh_pfc_err("func_gpio %s: name conflict\n", 10408c2ecf20Sopenharmony_ci func->name); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci if (sh_pfc_check_enum(drvname, func->enum_id)) 10438c2ecf20Sopenharmony_ci sh_pfc_err("%s enum_id %u conflict\n", func->name, 10448c2ecf20Sopenharmony_ci func->enum_id); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci#endif 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic void __init sh_pfc_check_driver(const struct platform_driver *pdrv) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci unsigned int i; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci sh_pfc_regs = kcalloc(SH_PFC_MAX_REGS, sizeof(*sh_pfc_regs), 10548c2ecf20Sopenharmony_ci GFP_KERNEL); 10558c2ecf20Sopenharmony_ci if (!sh_pfc_regs) 10568c2ecf20Sopenharmony_ci return; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci sh_pfc_enums = kcalloc(SH_PFC_MAX_ENUMS, sizeof(*sh_pfc_enums), 10598c2ecf20Sopenharmony_ci GFP_KERNEL); 10608c2ecf20Sopenharmony_ci if (!sh_pfc_enums) 10618c2ecf20Sopenharmony_ci goto free_regs; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci pr_warn("Checking builtin pinmux tables\n"); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci for (i = 0; pdrv->id_table[i].name[0]; i++) 10668c2ecf20Sopenharmony_ci sh_pfc_check_info((void *)pdrv->id_table[i].driver_data); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 10698c2ecf20Sopenharmony_ci for (i = 0; pdrv->driver.of_match_table[i].compatible[0]; i++) 10708c2ecf20Sopenharmony_ci sh_pfc_check_info(pdrv->driver.of_match_table[i].data); 10718c2ecf20Sopenharmony_ci#endif 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci pr_warn("Detected %u errors and %u warnings\n", sh_pfc_errors, 10748c2ecf20Sopenharmony_ci sh_pfc_warnings); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci kfree(sh_pfc_enums); 10778c2ecf20Sopenharmony_cifree_regs: 10788c2ecf20Sopenharmony_ci kfree(sh_pfc_regs); 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci#else /* !DEBUG */ 10828c2ecf20Sopenharmony_cistatic inline void sh_pfc_check_driver(struct platform_driver *pdrv) {} 10838c2ecf20Sopenharmony_ci#endif /* !DEBUG */ 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 10868c2ecf20Sopenharmony_cistatic const void *sh_pfc_quirk_match(void) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci#if defined(CONFIG_PINCTRL_PFC_R8A77950) || \ 10898c2ecf20Sopenharmony_ci defined(CONFIG_PINCTRL_PFC_R8A77951) 10908c2ecf20Sopenharmony_ci const struct soc_device_attribute *match; 10918c2ecf20Sopenharmony_ci static const struct soc_device_attribute quirks[] = { 10928c2ecf20Sopenharmony_ci { 10938c2ecf20Sopenharmony_ci .soc_id = "r8a7795", .revision = "ES1.*", 10948c2ecf20Sopenharmony_ci .data = &r8a77950_pinmux_info, 10958c2ecf20Sopenharmony_ci }, 10968c2ecf20Sopenharmony_ci { 10978c2ecf20Sopenharmony_ci .soc_id = "r8a7795", 10988c2ecf20Sopenharmony_ci .data = &r8a77951_pinmux_info, 10998c2ecf20Sopenharmony_ci }, 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci { /* sentinel */ } 11028c2ecf20Sopenharmony_ci }; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci match = soc_device_match(quirks); 11058c2ecf20Sopenharmony_ci if (match) 11068c2ecf20Sopenharmony_ci return match->data ?: ERR_PTR(-ENODEV); 11078c2ecf20Sopenharmony_ci#endif /* CONFIG_PINCTRL_PFC_R8A77950 || CONFIG_PINCTRL_PFC_R8A77951 */ 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci return NULL; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int sh_pfc_probe(struct platform_device *pdev) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci const struct sh_pfc_soc_info *info; 11168c2ecf20Sopenharmony_ci struct sh_pfc *pfc; 11178c2ecf20Sopenharmony_ci int ret; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 11208c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 11218c2ecf20Sopenharmony_ci info = sh_pfc_quirk_match(); 11228c2ecf20Sopenharmony_ci if (IS_ERR(info)) 11238c2ecf20Sopenharmony_ci return PTR_ERR(info); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (!info) 11268c2ecf20Sopenharmony_ci info = of_device_get_match_data(&pdev->dev); 11278c2ecf20Sopenharmony_ci } else 11288c2ecf20Sopenharmony_ci#endif 11298c2ecf20Sopenharmony_ci info = (const void *)platform_get_device_id(pdev)->driver_data; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci pfc = devm_kzalloc(&pdev->dev, sizeof(*pfc), GFP_KERNEL); 11328c2ecf20Sopenharmony_ci if (pfc == NULL) 11338c2ecf20Sopenharmony_ci return -ENOMEM; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci pfc->info = info; 11368c2ecf20Sopenharmony_ci pfc->dev = &pdev->dev; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci ret = sh_pfc_map_resources(pfc, pdev); 11398c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 11408c2ecf20Sopenharmony_ci return ret; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci spin_lock_init(&pfc->lock); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (info->ops && info->ops->init) { 11458c2ecf20Sopenharmony_ci ret = info->ops->init(pfc); 11468c2ecf20Sopenharmony_ci if (ret < 0) 11478c2ecf20Sopenharmony_ci return ret; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* .init() may have overridden pfc->info */ 11508c2ecf20Sopenharmony_ci info = pfc->info; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci ret = sh_pfc_suspend_init(pfc); 11548c2ecf20Sopenharmony_ci if (ret) 11558c2ecf20Sopenharmony_ci return ret; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci /* Enable dummy states for those platforms without pinctrl support */ 11588c2ecf20Sopenharmony_ci if (!of_have_populated_dt()) 11598c2ecf20Sopenharmony_ci pinctrl_provide_dummies(); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci ret = sh_pfc_init_ranges(pfc); 11628c2ecf20Sopenharmony_ci if (ret < 0) 11638c2ecf20Sopenharmony_ci return ret; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* 11668c2ecf20Sopenharmony_ci * Initialize pinctrl bindings first 11678c2ecf20Sopenharmony_ci */ 11688c2ecf20Sopenharmony_ci ret = sh_pfc_register_pinctrl(pfc); 11698c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) 11708c2ecf20Sopenharmony_ci return ret; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_SH_PFC_GPIO 11738c2ecf20Sopenharmony_ci /* 11748c2ecf20Sopenharmony_ci * Then the GPIO chip 11758c2ecf20Sopenharmony_ci */ 11768c2ecf20Sopenharmony_ci ret = sh_pfc_register_gpiochip(pfc); 11778c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) { 11788c2ecf20Sopenharmony_ci /* 11798c2ecf20Sopenharmony_ci * If the GPIO chip fails to come up we still leave the 11808c2ecf20Sopenharmony_ci * PFC state as it is, given that there are already 11818c2ecf20Sopenharmony_ci * extant users of it that have succeeded by this point. 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_ci dev_notice(pfc->dev, "failed to init GPIO chip, ignoring...\n"); 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci#endif 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pfc); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci dev_info(pfc->dev, "%s support registered\n", info->name); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci} 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic const struct platform_device_id sh_pfc_id_table[] = { 11958c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7203 11968c2ecf20Sopenharmony_ci { "pfc-sh7203", (kernel_ulong_t)&sh7203_pinmux_info }, 11978c2ecf20Sopenharmony_ci#endif 11988c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7264 11998c2ecf20Sopenharmony_ci { "pfc-sh7264", (kernel_ulong_t)&sh7264_pinmux_info }, 12008c2ecf20Sopenharmony_ci#endif 12018c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7269 12028c2ecf20Sopenharmony_ci { "pfc-sh7269", (kernel_ulong_t)&sh7269_pinmux_info }, 12038c2ecf20Sopenharmony_ci#endif 12048c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7720 12058c2ecf20Sopenharmony_ci { "pfc-sh7720", (kernel_ulong_t)&sh7720_pinmux_info }, 12068c2ecf20Sopenharmony_ci#endif 12078c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7722 12088c2ecf20Sopenharmony_ci { "pfc-sh7722", (kernel_ulong_t)&sh7722_pinmux_info }, 12098c2ecf20Sopenharmony_ci#endif 12108c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7723 12118c2ecf20Sopenharmony_ci { "pfc-sh7723", (kernel_ulong_t)&sh7723_pinmux_info }, 12128c2ecf20Sopenharmony_ci#endif 12138c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7724 12148c2ecf20Sopenharmony_ci { "pfc-sh7724", (kernel_ulong_t)&sh7724_pinmux_info }, 12158c2ecf20Sopenharmony_ci#endif 12168c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7734 12178c2ecf20Sopenharmony_ci { "pfc-sh7734", (kernel_ulong_t)&sh7734_pinmux_info }, 12188c2ecf20Sopenharmony_ci#endif 12198c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7757 12208c2ecf20Sopenharmony_ci { "pfc-sh7757", (kernel_ulong_t)&sh7757_pinmux_info }, 12218c2ecf20Sopenharmony_ci#endif 12228c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7785 12238c2ecf20Sopenharmony_ci { "pfc-sh7785", (kernel_ulong_t)&sh7785_pinmux_info }, 12248c2ecf20Sopenharmony_ci#endif 12258c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7786 12268c2ecf20Sopenharmony_ci { "pfc-sh7786", (kernel_ulong_t)&sh7786_pinmux_info }, 12278c2ecf20Sopenharmony_ci#endif 12288c2ecf20Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SHX3 12298c2ecf20Sopenharmony_ci { "pfc-shx3", (kernel_ulong_t)&shx3_pinmux_info }, 12308c2ecf20Sopenharmony_ci#endif 12318c2ecf20Sopenharmony_ci { }, 12328c2ecf20Sopenharmony_ci}; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic struct platform_driver sh_pfc_driver = { 12358c2ecf20Sopenharmony_ci .probe = sh_pfc_probe, 12368c2ecf20Sopenharmony_ci .id_table = sh_pfc_id_table, 12378c2ecf20Sopenharmony_ci .driver = { 12388c2ecf20Sopenharmony_ci .name = DRV_NAME, 12398c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(sh_pfc_of_table), 12408c2ecf20Sopenharmony_ci .pm = DEV_PM_OPS, 12418c2ecf20Sopenharmony_ci }, 12428c2ecf20Sopenharmony_ci}; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic int __init sh_pfc_init(void) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci sh_pfc_check_driver(&sh_pfc_driver); 12478c2ecf20Sopenharmony_ci return platform_driver_register(&sh_pfc_driver); 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_cipostcore_initcall(sh_pfc_init); 1250