162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Pin Control and GPIO driver for SuperH Pin Function Controller. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Magnus Damm, Paul Mundt, Laurent Pinchart 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2008 Magnus Damm 862306a36Sopenharmony_ci * Copyright (C) 2009 - 2012 Paul Mundt 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define DRV_NAME "sh-pfc" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/bitops.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/ioport.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/math.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/pinctrl/machine.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/psci.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/sys_soc.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "core.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int sh_pfc_map_resources(struct sh_pfc *pfc, 3162306a36Sopenharmony_ci struct platform_device *pdev) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct sh_pfc_window *windows; 3462306a36Sopenharmony_ci unsigned int *irqs = NULL; 3562306a36Sopenharmony_ci unsigned int num_windows; 3662306a36Sopenharmony_ci struct resource *res; 3762306a36Sopenharmony_ci unsigned int i; 3862306a36Sopenharmony_ci int num_irqs; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Count the MEM and IRQ resources. */ 4162306a36Sopenharmony_ci for (num_windows = 0;; num_windows++) { 4262306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, num_windows); 4362306a36Sopenharmony_ci if (!res) 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci if (num_windows == 0) 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci num_irqs = platform_irq_count(pdev); 5062306a36Sopenharmony_ci if (num_irqs < 0) 5162306a36Sopenharmony_ci return num_irqs; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Allocate memory windows and IRQs arrays. */ 5462306a36Sopenharmony_ci windows = devm_kcalloc(pfc->dev, num_windows, sizeof(*windows), 5562306a36Sopenharmony_ci GFP_KERNEL); 5662306a36Sopenharmony_ci if (windows == NULL) 5762306a36Sopenharmony_ci return -ENOMEM; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pfc->num_windows = num_windows; 6062306a36Sopenharmony_ci pfc->windows = windows; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (num_irqs) { 6362306a36Sopenharmony_ci irqs = devm_kcalloc(pfc->dev, num_irqs, sizeof(*irqs), 6462306a36Sopenharmony_ci GFP_KERNEL); 6562306a36Sopenharmony_ci if (irqs == NULL) 6662306a36Sopenharmony_ci return -ENOMEM; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci pfc->num_irqs = num_irqs; 6962306a36Sopenharmony_ci pfc->irqs = irqs; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Fill them. */ 7362306a36Sopenharmony_ci for (i = 0; i < num_windows; i++) { 7462306a36Sopenharmony_ci windows->virt = devm_platform_get_and_ioremap_resource(pdev, i, &res); 7562306a36Sopenharmony_ci if (IS_ERR(windows->virt)) 7662306a36Sopenharmony_ci return -ENOMEM; 7762306a36Sopenharmony_ci windows->phys = res->start; 7862306a36Sopenharmony_ci windows->size = resource_size(res); 7962306a36Sopenharmony_ci windows++; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci for (i = 0; i < num_irqs; i++) 8262306a36Sopenharmony_ci *irqs++ = platform_get_irq(pdev, i); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, u32 reg) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct sh_pfc_window *window; 9062306a36Sopenharmony_ci phys_addr_t address = reg; 9162306a36Sopenharmony_ci unsigned int i; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* scan through physical windows and convert address */ 9462306a36Sopenharmony_ci for (i = 0; i < pfc->num_windows; i++) { 9562306a36Sopenharmony_ci window = pfc->windows + i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (address < window->phys) 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (address >= (window->phys + window->size)) 10162306a36Sopenharmony_ci continue; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return window->virt + (address - window->phys); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci BUG(); 10762306a36Sopenharmony_ci return NULL; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciint sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci unsigned int offset; 11362306a36Sopenharmony_ci unsigned int i; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci for (i = 0, offset = 0; i < pfc->nr_ranges; ++i) { 11662306a36Sopenharmony_ci const struct sh_pfc_pin_range *range = &pfc->ranges[i]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (pin <= range->end) 11962306a36Sopenharmony_ci return pin >= range->start 12062306a36Sopenharmony_ci ? offset + pin - range->start : -1; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci offset += range->end - range->start + 1; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return -EINVAL; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int sh_pfc_enum_in_range(u16 enum_id, const struct pinmux_range *r) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci if (enum_id < r->begin) 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (enum_id > r->end) 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return 1; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciu32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci switch (reg_width) { 14262306a36Sopenharmony_ci case 8: 14362306a36Sopenharmony_ci return ioread8(mapped_reg); 14462306a36Sopenharmony_ci case 16: 14562306a36Sopenharmony_ci return ioread16(mapped_reg); 14662306a36Sopenharmony_ci case 32: 14762306a36Sopenharmony_ci return ioread32(mapped_reg); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci BUG(); 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_civoid sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width, 15562306a36Sopenharmony_ci u32 data) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci switch (reg_width) { 15862306a36Sopenharmony_ci case 8: 15962306a36Sopenharmony_ci iowrite8(data, mapped_reg); 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci case 16: 16262306a36Sopenharmony_ci iowrite16(data, mapped_reg); 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci case 32: 16562306a36Sopenharmony_ci iowrite32(data, mapped_reg); 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci BUG(); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciu32 sh_pfc_read(struct sh_pfc *pfc, u32 reg) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci return sh_pfc_read_raw_reg(sh_pfc_phys_to_virt(pfc, reg), 32); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void sh_pfc_unlock_reg(struct sh_pfc *pfc, u32 reg, u32 data) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci u32 unlock; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!pfc->info->unlock_reg) 18262306a36Sopenharmony_ci return; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (pfc->info->unlock_reg >= 0x80000000UL) 18562306a36Sopenharmony_ci unlock = pfc->info->unlock_reg; 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci /* unlock_reg is a mask */ 18862306a36Sopenharmony_ci unlock = reg & ~pfc->info->unlock_reg; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci sh_pfc_write_raw_reg(sh_pfc_phys_to_virt(pfc, unlock), 32, ~data); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_civoid sh_pfc_write(struct sh_pfc *pfc, u32 reg, u32 data) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci sh_pfc_unlock_reg(pfc, reg, data); 19662306a36Sopenharmony_ci sh_pfc_write_raw_reg(sh_pfc_phys_to_virt(pfc, reg), 32, data); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void sh_pfc_config_reg_helper(struct sh_pfc *pfc, 20062306a36Sopenharmony_ci const struct pinmux_cfg_reg *crp, 20162306a36Sopenharmony_ci unsigned int in_pos, 20262306a36Sopenharmony_ci void __iomem **mapped_regp, u32 *maskp, 20362306a36Sopenharmony_ci unsigned int *posp) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci unsigned int k; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci *mapped_regp = sh_pfc_phys_to_virt(pfc, crp->reg); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (crp->field_width) { 21062306a36Sopenharmony_ci *maskp = (1 << crp->field_width) - 1; 21162306a36Sopenharmony_ci *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci *maskp = (1 << crp->var_field_width[in_pos]) - 1; 21462306a36Sopenharmony_ci *posp = crp->reg_width; 21562306a36Sopenharmony_ci for (k = 0; k <= in_pos; k++) 21662306a36Sopenharmony_ci *posp -= abs(crp->var_field_width[k]); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void sh_pfc_write_config_reg(struct sh_pfc *pfc, 22162306a36Sopenharmony_ci const struct pinmux_cfg_reg *crp, 22262306a36Sopenharmony_ci unsigned int field, u32 value) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci void __iomem *mapped_reg; 22562306a36Sopenharmony_ci unsigned int pos; 22662306a36Sopenharmony_ci u32 mask, data; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci sh_pfc_config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci dev_dbg(pfc->dev, "write_reg addr = %x, value = 0x%x, field = %u, " 23162306a36Sopenharmony_ci "r_width = %u, f_width = %u\n", 23262306a36Sopenharmony_ci crp->reg, value, field, crp->reg_width, hweight32(mask)); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mask = ~(mask << pos); 23562306a36Sopenharmony_ci value = value << pos; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci data = sh_pfc_read_raw_reg(mapped_reg, crp->reg_width); 23862306a36Sopenharmony_ci data &= mask; 23962306a36Sopenharmony_ci data |= value; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci sh_pfc_unlock_reg(pfc, crp->reg, data); 24262306a36Sopenharmony_ci sh_pfc_write_raw_reg(mapped_reg, crp->reg_width, data); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int sh_pfc_get_config_reg(struct sh_pfc *pfc, u16 enum_id, 24662306a36Sopenharmony_ci const struct pinmux_cfg_reg **crp, 24762306a36Sopenharmony_ci unsigned int *fieldp, u32 *valuep) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci unsigned int k = 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci while (1) { 25262306a36Sopenharmony_ci const struct pinmux_cfg_reg *config_reg = 25362306a36Sopenharmony_ci pfc->info->cfg_regs + k; 25462306a36Sopenharmony_ci unsigned int r_width = config_reg->reg_width; 25562306a36Sopenharmony_ci unsigned int f_width = config_reg->field_width; 25662306a36Sopenharmony_ci unsigned int curr_width; 25762306a36Sopenharmony_ci unsigned int bit_pos; 25862306a36Sopenharmony_ci unsigned int pos = 0; 25962306a36Sopenharmony_ci unsigned int m = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!r_width) 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width, m++) { 26562306a36Sopenharmony_ci u32 ncomb; 26662306a36Sopenharmony_ci u32 n; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (f_width) { 26962306a36Sopenharmony_ci curr_width = f_width; 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci curr_width = abs(config_reg->var_field_width[m]); 27262306a36Sopenharmony_ci if (config_reg->var_field_width[m] < 0) 27362306a36Sopenharmony_ci continue; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ncomb = 1 << curr_width; 27762306a36Sopenharmony_ci for (n = 0; n < ncomb; n++) { 27862306a36Sopenharmony_ci if (config_reg->enum_ids[pos + n] == enum_id) { 27962306a36Sopenharmony_ci *crp = config_reg; 28062306a36Sopenharmony_ci *fieldp = m; 28162306a36Sopenharmony_ci *valuep = n; 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci pos += ncomb; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci k++; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int sh_pfc_mark_to_enum(struct sh_pfc *pfc, u16 mark, int pos, 29462306a36Sopenharmony_ci u16 *enum_idp) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci const u16 *data = pfc->info->pinmux_data; 29762306a36Sopenharmony_ci unsigned int k; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (pos) { 30062306a36Sopenharmony_ci *enum_idp = data[pos + 1]; 30162306a36Sopenharmony_ci return pos + 1; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci for (k = 0; k < pfc->info->pinmux_data_size; k++) { 30562306a36Sopenharmony_ci if (data[k] == mark) { 30662306a36Sopenharmony_ci *enum_idp = data[k + 1]; 30762306a36Sopenharmony_ci return k + 1; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci dev_err(pfc->dev, "cannot locate data/mark enum_id for mark %d\n", 31262306a36Sopenharmony_ci mark); 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciint sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci const struct pinmux_range *range; 31962306a36Sopenharmony_ci int pos = 0; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci switch (pinmux_type) { 32262306a36Sopenharmony_ci case PINMUX_TYPE_GPIO: 32362306a36Sopenharmony_ci case PINMUX_TYPE_FUNCTION: 32462306a36Sopenharmony_ci range = NULL; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_SH_PFC_GPIO 32862306a36Sopenharmony_ci case PINMUX_TYPE_OUTPUT: 32962306a36Sopenharmony_ci range = &pfc->info->output; 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci case PINMUX_TYPE_INPUT: 33362306a36Sopenharmony_ci range = &pfc->info->input; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci#endif /* CONFIG_PINCTRL_SH_PFC_GPIO */ 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci default: 33862306a36Sopenharmony_ci return -EINVAL; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Iterate over all the configuration fields we need to update. */ 34262306a36Sopenharmony_ci while (1) { 34362306a36Sopenharmony_ci const struct pinmux_cfg_reg *cr; 34462306a36Sopenharmony_ci unsigned int field; 34562306a36Sopenharmony_ci u16 enum_id; 34662306a36Sopenharmony_ci u32 value; 34762306a36Sopenharmony_ci int in_range; 34862306a36Sopenharmony_ci int ret; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci pos = sh_pfc_mark_to_enum(pfc, mark, pos, &enum_id); 35162306a36Sopenharmony_ci if (pos < 0) 35262306a36Sopenharmony_ci return pos; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (!enum_id) 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Check if the configuration field selects a function. If it 35862306a36Sopenharmony_ci * doesn't, skip the field if it's not applicable to the 35962306a36Sopenharmony_ci * requested pinmux type. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci in_range = sh_pfc_enum_in_range(enum_id, &pfc->info->function); 36262306a36Sopenharmony_ci if (!in_range) { 36362306a36Sopenharmony_ci if (pinmux_type == PINMUX_TYPE_FUNCTION) { 36462306a36Sopenharmony_ci /* Functions are allowed to modify all 36562306a36Sopenharmony_ci * fields. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci in_range = 1; 36862306a36Sopenharmony_ci } else if (pinmux_type != PINMUX_TYPE_GPIO) { 36962306a36Sopenharmony_ci /* Input/output types can only modify fields 37062306a36Sopenharmony_ci * that correspond to their respective ranges. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci in_range = sh_pfc_enum_in_range(enum_id, range); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * special case pass through for fixed 37662306a36Sopenharmony_ci * input-only or output-only pins without 37762306a36Sopenharmony_ci * function enum register association. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci if (in_range && enum_id == range->force) 38062306a36Sopenharmony_ci continue; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci /* GPIOs are only allowed to modify function fields. */ 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (!in_range) 38662306a36Sopenharmony_ci continue; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ret = sh_pfc_get_config_reg(pfc, enum_id, &cr, &field, &value); 38962306a36Sopenharmony_ci if (ret < 0) 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci sh_pfc_write_config_reg(pfc, cr, field, value); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int sh_pfc_init_ranges(struct sh_pfc *pfc) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct sh_pfc_pin_range *range; 40162306a36Sopenharmony_ci unsigned int nr_ranges; 40262306a36Sopenharmony_ci unsigned int i; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (pfc->info->pins[0].pin == (u16)-1) { 40562306a36Sopenharmony_ci /* Pin number -1 denotes that the SoC doesn't report pin numbers 40662306a36Sopenharmony_ci * in its pin arrays yet. Consider the pin numbers range as 40762306a36Sopenharmony_ci * continuous and allocate a single range. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci pfc->nr_ranges = 1; 41062306a36Sopenharmony_ci pfc->ranges = devm_kzalloc(pfc->dev, sizeof(*pfc->ranges), 41162306a36Sopenharmony_ci GFP_KERNEL); 41262306a36Sopenharmony_ci if (pfc->ranges == NULL) 41362306a36Sopenharmony_ci return -ENOMEM; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pfc->ranges->start = 0; 41662306a36Sopenharmony_ci pfc->ranges->end = pfc->info->nr_pins - 1; 41762306a36Sopenharmony_ci pfc->nr_gpio_pins = pfc->info->nr_pins; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Count, allocate and fill the ranges. The PFC SoC data pins array must 42362306a36Sopenharmony_ci * be sorted by pin numbers, and pins without a GPIO port must come 42462306a36Sopenharmony_ci * last. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci for (i = 1, nr_ranges = 1; i < pfc->info->nr_pins; ++i) { 42762306a36Sopenharmony_ci if (pfc->info->pins[i-1].pin != pfc->info->pins[i].pin - 1) 42862306a36Sopenharmony_ci nr_ranges++; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci pfc->nr_ranges = nr_ranges; 43262306a36Sopenharmony_ci pfc->ranges = devm_kcalloc(pfc->dev, nr_ranges, sizeof(*pfc->ranges), 43362306a36Sopenharmony_ci GFP_KERNEL); 43462306a36Sopenharmony_ci if (pfc->ranges == NULL) 43562306a36Sopenharmony_ci return -ENOMEM; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci range = pfc->ranges; 43862306a36Sopenharmony_ci range->start = pfc->info->pins[0].pin; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci for (i = 1; i < pfc->info->nr_pins; ++i) { 44162306a36Sopenharmony_ci if (pfc->info->pins[i-1].pin == pfc->info->pins[i].pin - 1) 44262306a36Sopenharmony_ci continue; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci range->end = pfc->info->pins[i-1].pin; 44562306a36Sopenharmony_ci if (!(pfc->info->pins[i-1].configs & SH_PFC_PIN_CFG_NO_GPIO)) 44662306a36Sopenharmony_ci pfc->nr_gpio_pins = range->end + 1; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci range++; 44962306a36Sopenharmony_ci range->start = pfc->info->pins[i].pin; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci range->end = pfc->info->pins[i-1].pin; 45362306a36Sopenharmony_ci if (!(pfc->info->pins[i-1].configs & SH_PFC_PIN_CFG_NO_GPIO)) 45462306a36Sopenharmony_ci pfc->nr_gpio_pins = range->end + 1; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci#ifdef CONFIG_OF 46062306a36Sopenharmony_cistatic const struct of_device_id sh_pfc_of_table[] = { 46162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_EMEV2 46262306a36Sopenharmony_ci { 46362306a36Sopenharmony_ci .compatible = "renesas,pfc-emev2", 46462306a36Sopenharmony_ci .data = &emev2_pinmux_info, 46562306a36Sopenharmony_ci }, 46662306a36Sopenharmony_ci#endif 46762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A73A4 46862306a36Sopenharmony_ci { 46962306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a73a4", 47062306a36Sopenharmony_ci .data = &r8a73a4_pinmux_info, 47162306a36Sopenharmony_ci }, 47262306a36Sopenharmony_ci#endif 47362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7740 47462306a36Sopenharmony_ci { 47562306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7740", 47662306a36Sopenharmony_ci .data = &r8a7740_pinmux_info, 47762306a36Sopenharmony_ci }, 47862306a36Sopenharmony_ci#endif 47962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7742 48062306a36Sopenharmony_ci { 48162306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7742", 48262306a36Sopenharmony_ci .data = &r8a7742_pinmux_info, 48362306a36Sopenharmony_ci }, 48462306a36Sopenharmony_ci#endif 48562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7743 48662306a36Sopenharmony_ci { 48762306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7743", 48862306a36Sopenharmony_ci .data = &r8a7743_pinmux_info, 48962306a36Sopenharmony_ci }, 49062306a36Sopenharmony_ci#endif 49162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7744 49262306a36Sopenharmony_ci { 49362306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7744", 49462306a36Sopenharmony_ci .data = &r8a7744_pinmux_info, 49562306a36Sopenharmony_ci }, 49662306a36Sopenharmony_ci#endif 49762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7745 49862306a36Sopenharmony_ci { 49962306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7745", 50062306a36Sopenharmony_ci .data = &r8a7745_pinmux_info, 50162306a36Sopenharmony_ci }, 50262306a36Sopenharmony_ci#endif 50362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77470 50462306a36Sopenharmony_ci { 50562306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a77470", 50662306a36Sopenharmony_ci .data = &r8a77470_pinmux_info, 50762306a36Sopenharmony_ci }, 50862306a36Sopenharmony_ci#endif 50962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774A1 51062306a36Sopenharmony_ci { 51162306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a774a1", 51262306a36Sopenharmony_ci .data = &r8a774a1_pinmux_info, 51362306a36Sopenharmony_ci }, 51462306a36Sopenharmony_ci#endif 51562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774B1 51662306a36Sopenharmony_ci { 51762306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a774b1", 51862306a36Sopenharmony_ci .data = &r8a774b1_pinmux_info, 51962306a36Sopenharmony_ci }, 52062306a36Sopenharmony_ci#endif 52162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774C0 52262306a36Sopenharmony_ci { 52362306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a774c0", 52462306a36Sopenharmony_ci .data = &r8a774c0_pinmux_info, 52562306a36Sopenharmony_ci }, 52662306a36Sopenharmony_ci#endif 52762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A774E1 52862306a36Sopenharmony_ci { 52962306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a774e1", 53062306a36Sopenharmony_ci .data = &r8a774e1_pinmux_info, 53162306a36Sopenharmony_ci }, 53262306a36Sopenharmony_ci#endif 53362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7778 53462306a36Sopenharmony_ci { 53562306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7778", 53662306a36Sopenharmony_ci .data = &r8a7778_pinmux_info, 53762306a36Sopenharmony_ci }, 53862306a36Sopenharmony_ci#endif 53962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7779 54062306a36Sopenharmony_ci { 54162306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7779", 54262306a36Sopenharmony_ci .data = &r8a7779_pinmux_info, 54362306a36Sopenharmony_ci }, 54462306a36Sopenharmony_ci#endif 54562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7790 54662306a36Sopenharmony_ci { 54762306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7790", 54862306a36Sopenharmony_ci .data = &r8a7790_pinmux_info, 54962306a36Sopenharmony_ci }, 55062306a36Sopenharmony_ci#endif 55162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7791 55262306a36Sopenharmony_ci { 55362306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7791", 55462306a36Sopenharmony_ci .data = &r8a7791_pinmux_info, 55562306a36Sopenharmony_ci }, 55662306a36Sopenharmony_ci#endif 55762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7792 55862306a36Sopenharmony_ci { 55962306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7792", 56062306a36Sopenharmony_ci .data = &r8a7792_pinmux_info, 56162306a36Sopenharmony_ci }, 56262306a36Sopenharmony_ci#endif 56362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7793 56462306a36Sopenharmony_ci { 56562306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7793", 56662306a36Sopenharmony_ci .data = &r8a7793_pinmux_info, 56762306a36Sopenharmony_ci }, 56862306a36Sopenharmony_ci#endif 56962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A7794 57062306a36Sopenharmony_ci { 57162306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7794", 57262306a36Sopenharmony_ci .data = &r8a7794_pinmux_info, 57362306a36Sopenharmony_ci }, 57462306a36Sopenharmony_ci#endif 57562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77951 57662306a36Sopenharmony_ci { 57762306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7795", 57862306a36Sopenharmony_ci .data = &r8a77951_pinmux_info, 57962306a36Sopenharmony_ci }, 58062306a36Sopenharmony_ci#endif 58162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77960 58262306a36Sopenharmony_ci { 58362306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a7796", 58462306a36Sopenharmony_ci .data = &r8a77960_pinmux_info, 58562306a36Sopenharmony_ci }, 58662306a36Sopenharmony_ci#endif 58762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77961 58862306a36Sopenharmony_ci { 58962306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a77961", 59062306a36Sopenharmony_ci .data = &r8a77961_pinmux_info, 59162306a36Sopenharmony_ci }, 59262306a36Sopenharmony_ci#endif 59362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77965 59462306a36Sopenharmony_ci { 59562306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a77965", 59662306a36Sopenharmony_ci .data = &r8a77965_pinmux_info, 59762306a36Sopenharmony_ci }, 59862306a36Sopenharmony_ci#endif 59962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77970 60062306a36Sopenharmony_ci { 60162306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a77970", 60262306a36Sopenharmony_ci .data = &r8a77970_pinmux_info, 60362306a36Sopenharmony_ci }, 60462306a36Sopenharmony_ci#endif 60562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77980 60662306a36Sopenharmony_ci { 60762306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a77980", 60862306a36Sopenharmony_ci .data = &r8a77980_pinmux_info, 60962306a36Sopenharmony_ci }, 61062306a36Sopenharmony_ci#endif 61162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77990 61262306a36Sopenharmony_ci { 61362306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a77990", 61462306a36Sopenharmony_ci .data = &r8a77990_pinmux_info, 61562306a36Sopenharmony_ci }, 61662306a36Sopenharmony_ci#endif 61762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A77995 61862306a36Sopenharmony_ci { 61962306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a77995", 62062306a36Sopenharmony_ci .data = &r8a77995_pinmux_info, 62162306a36Sopenharmony_ci }, 62262306a36Sopenharmony_ci#endif 62362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A779A0 62462306a36Sopenharmony_ci { 62562306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a779a0", 62662306a36Sopenharmony_ci .data = &r8a779a0_pinmux_info, 62762306a36Sopenharmony_ci }, 62862306a36Sopenharmony_ci#endif 62962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A779F0 63062306a36Sopenharmony_ci { 63162306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a779f0", 63262306a36Sopenharmony_ci .data = &r8a779f0_pinmux_info, 63362306a36Sopenharmony_ci }, 63462306a36Sopenharmony_ci#endif 63562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_R8A779G0 63662306a36Sopenharmony_ci { 63762306a36Sopenharmony_ci .compatible = "renesas,pfc-r8a779g0", 63862306a36Sopenharmony_ci .data = &r8a779g0_pinmux_info, 63962306a36Sopenharmony_ci }, 64062306a36Sopenharmony_ci#endif 64162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH73A0 64262306a36Sopenharmony_ci { 64362306a36Sopenharmony_ci .compatible = "renesas,pfc-sh73a0", 64462306a36Sopenharmony_ci .data = &sh73a0_pinmux_info, 64562306a36Sopenharmony_ci }, 64662306a36Sopenharmony_ci#endif 64762306a36Sopenharmony_ci { /* sentinel */ } 64862306a36Sopenharmony_ci}; 64962306a36Sopenharmony_ci#endif 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci#if defined(CONFIG_ARM_PSCI_FW) 65262306a36Sopenharmony_cistatic void sh_pfc_nop_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic void sh_pfc_save_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci pfc->saved_regs[idx] = sh_pfc_read(pfc, reg); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic void sh_pfc_restore_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci sh_pfc_write(pfc, reg, pfc->saved_regs[idx]); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic unsigned int sh_pfc_walk_regs(struct sh_pfc *pfc, 66762306a36Sopenharmony_ci void (*do_reg)(struct sh_pfc *pfc, u32 reg, unsigned int idx)) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci unsigned int i, n = 0; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (pfc->info->cfg_regs) 67262306a36Sopenharmony_ci for (i = 0; pfc->info->cfg_regs[i].reg; i++) 67362306a36Sopenharmony_ci do_reg(pfc, pfc->info->cfg_regs[i].reg, n++); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (pfc->info->drive_regs) 67662306a36Sopenharmony_ci for (i = 0; pfc->info->drive_regs[i].reg; i++) 67762306a36Sopenharmony_ci do_reg(pfc, pfc->info->drive_regs[i].reg, n++); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (pfc->info->bias_regs) 68062306a36Sopenharmony_ci for (i = 0; pfc->info->bias_regs[i].puen || 68162306a36Sopenharmony_ci pfc->info->bias_regs[i].pud; i++) { 68262306a36Sopenharmony_ci if (pfc->info->bias_regs[i].puen) 68362306a36Sopenharmony_ci do_reg(pfc, pfc->info->bias_regs[i].puen, n++); 68462306a36Sopenharmony_ci if (pfc->info->bias_regs[i].pud) 68562306a36Sopenharmony_ci do_reg(pfc, pfc->info->bias_regs[i].pud, n++); 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (pfc->info->ioctrl_regs) 68962306a36Sopenharmony_ci for (i = 0; pfc->info->ioctrl_regs[i].reg; i++) 69062306a36Sopenharmony_ci do_reg(pfc, pfc->info->ioctrl_regs[i].reg, n++); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return n; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int sh_pfc_suspend_init(struct sh_pfc *pfc) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci unsigned int n; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* This is the best we can do to check for the presence of PSCI */ 70062306a36Sopenharmony_ci if (!psci_ops.cpu_suspend) 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci n = sh_pfc_walk_regs(pfc, sh_pfc_nop_reg); 70462306a36Sopenharmony_ci if (!n) 70562306a36Sopenharmony_ci return 0; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci pfc->saved_regs = devm_kmalloc_array(pfc->dev, n, 70862306a36Sopenharmony_ci sizeof(*pfc->saved_regs), 70962306a36Sopenharmony_ci GFP_KERNEL); 71062306a36Sopenharmony_ci if (!pfc->saved_regs) 71162306a36Sopenharmony_ci return -ENOMEM; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci dev_dbg(pfc->dev, "Allocated space to save %u regs\n", n); 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int sh_pfc_suspend_noirq(struct device *dev) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct sh_pfc *pfc = dev_get_drvdata(dev); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (pfc->saved_regs) 72262306a36Sopenharmony_ci sh_pfc_walk_regs(pfc, sh_pfc_save_reg); 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int sh_pfc_resume_noirq(struct device *dev) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct sh_pfc *pfc = dev_get_drvdata(dev); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (pfc->saved_regs) 73162306a36Sopenharmony_ci sh_pfc_walk_regs(pfc, sh_pfc_restore_reg); 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci#define pm_psci_sleep_ptr(_ptr) pm_sleep_ptr(_ptr) 73562306a36Sopenharmony_ci#else 73662306a36Sopenharmony_cistatic int sh_pfc_suspend_init(struct sh_pfc *pfc) { return 0; } 73762306a36Sopenharmony_cistatic int sh_pfc_suspend_noirq(struct device *dev) { return 0; } 73862306a36Sopenharmony_cistatic int sh_pfc_resume_noirq(struct device *dev) { return 0; } 73962306a36Sopenharmony_ci#define pm_psci_sleep_ptr(_ptr) PTR_IF(false, (_ptr)) 74062306a36Sopenharmony_ci#endif /* CONFIG_ARM_PSCI_FW */ 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic DEFINE_NOIRQ_DEV_PM_OPS(sh_pfc_pm, sh_pfc_suspend_noirq, sh_pfc_resume_noirq); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci#ifdef DEBUG 74562306a36Sopenharmony_ci#define SH_PFC_MAX_REGS 300 74662306a36Sopenharmony_ci#define SH_PFC_MAX_ENUMS 5000 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic unsigned int sh_pfc_errors __initdata; 74962306a36Sopenharmony_cistatic unsigned int sh_pfc_warnings __initdata; 75062306a36Sopenharmony_cistatic bool sh_pfc_bias_done __initdata; 75162306a36Sopenharmony_cistatic bool sh_pfc_drive_done __initdata; 75262306a36Sopenharmony_cistatic bool sh_pfc_power_done __initdata; 75362306a36Sopenharmony_cistatic struct { 75462306a36Sopenharmony_ci u32 reg; 75562306a36Sopenharmony_ci u32 bits; 75662306a36Sopenharmony_ci} *sh_pfc_regs __initdata; 75762306a36Sopenharmony_cistatic u32 sh_pfc_num_regs __initdata; 75862306a36Sopenharmony_cistatic u16 *sh_pfc_enums __initdata; 75962306a36Sopenharmony_cistatic u32 sh_pfc_num_enums __initdata; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci#define sh_pfc_err(fmt, ...) \ 76262306a36Sopenharmony_ci do { \ 76362306a36Sopenharmony_ci pr_err("%s: " fmt, drvname, ##__VA_ARGS__); \ 76462306a36Sopenharmony_ci sh_pfc_errors++; \ 76562306a36Sopenharmony_ci } while (0) 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci#define sh_pfc_err_once(type, fmt, ...) \ 76862306a36Sopenharmony_ci do { \ 76962306a36Sopenharmony_ci if (!sh_pfc_ ## type ## _done) { \ 77062306a36Sopenharmony_ci sh_pfc_ ## type ## _done = true; \ 77162306a36Sopenharmony_ci sh_pfc_err(fmt, ##__VA_ARGS__); \ 77262306a36Sopenharmony_ci } \ 77362306a36Sopenharmony_ci } while (0) 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci#define sh_pfc_warn(fmt, ...) \ 77662306a36Sopenharmony_ci do { \ 77762306a36Sopenharmony_ci pr_warn("%s: " fmt, drvname, ##__VA_ARGS__); \ 77862306a36Sopenharmony_ci sh_pfc_warnings++; \ 77962306a36Sopenharmony_ci } while (0) 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic bool __init is0s(const u16 *enum_ids, unsigned int n) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci unsigned int i; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci for (i = 0; i < n; i++) 78662306a36Sopenharmony_ci if (enum_ids[i]) 78762306a36Sopenharmony_ci return false; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return true; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic bool __init same_name(const char *a, const char *b) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci return a && b && !strcmp(a, b); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic void __init sh_pfc_check_reg(const char *drvname, u32 reg, u32 bits) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci unsigned int i; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci for (i = 0; i < sh_pfc_num_regs; i++) { 80262306a36Sopenharmony_ci if (reg != sh_pfc_regs[i].reg) 80362306a36Sopenharmony_ci continue; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (bits & sh_pfc_regs[i].bits) 80662306a36Sopenharmony_ci sh_pfc_err("reg 0x%x: bits 0x%x conflict\n", reg, 80762306a36Sopenharmony_ci bits & sh_pfc_regs[i].bits); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci sh_pfc_regs[i].bits |= bits; 81062306a36Sopenharmony_ci return; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (sh_pfc_num_regs == SH_PFC_MAX_REGS) { 81462306a36Sopenharmony_ci pr_warn_once("%s: Please increase SH_PFC_MAX_REGS\n", drvname); 81562306a36Sopenharmony_ci return; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci sh_pfc_regs[sh_pfc_num_regs].reg = reg; 81962306a36Sopenharmony_ci sh_pfc_regs[sh_pfc_num_regs].bits = bits; 82062306a36Sopenharmony_ci sh_pfc_num_regs++; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int __init sh_pfc_check_enum(const char *drvname, u16 enum_id) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci unsigned int i; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci for (i = 0; i < sh_pfc_num_enums; i++) { 82862306a36Sopenharmony_ci if (enum_id == sh_pfc_enums[i]) 82962306a36Sopenharmony_ci return -EINVAL; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (sh_pfc_num_enums == SH_PFC_MAX_ENUMS) { 83362306a36Sopenharmony_ci pr_warn_once("%s: Please increase SH_PFC_MAX_ENUMS\n", drvname); 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci sh_pfc_enums[sh_pfc_num_enums++] = enum_id; 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic void __init sh_pfc_check_reg_enums(const char *drvname, u32 reg, 84262306a36Sopenharmony_ci const u16 *enums, unsigned int n) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci unsigned int i; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci for (i = 0; i < n; i++) { 84762306a36Sopenharmony_ci if (enums[i] && sh_pfc_check_enum(drvname, enums[i])) 84862306a36Sopenharmony_ci sh_pfc_err("reg 0x%x enum_id %u conflict\n", reg, 84962306a36Sopenharmony_ci enums[i]); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic const struct sh_pfc_pin __init *sh_pfc_find_pin( 85462306a36Sopenharmony_ci const struct sh_pfc_soc_info *info, u32 reg, unsigned int pin) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci const char *drvname = info->name; 85762306a36Sopenharmony_ci unsigned int i; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (pin == SH_PFC_PIN_NONE) 86062306a36Sopenharmony_ci return NULL; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci for (i = 0; i < info->nr_pins; i++) { 86362306a36Sopenharmony_ci if (pin == info->pins[i].pin) 86462306a36Sopenharmony_ci return &info->pins[i]; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci sh_pfc_err("reg 0x%x: pin %u not found\n", reg, pin); 86862306a36Sopenharmony_ci return NULL; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic void __init sh_pfc_check_cfg_reg(const char *drvname, 87262306a36Sopenharmony_ci const struct pinmux_cfg_reg *cfg_reg) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci unsigned int i, n, rw, r; 87562306a36Sopenharmony_ci int fw; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci sh_pfc_check_reg(drvname, cfg_reg->reg, 87862306a36Sopenharmony_ci GENMASK(cfg_reg->reg_width - 1, 0)); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (cfg_reg->field_width) { 88162306a36Sopenharmony_ci fw = cfg_reg->field_width; 88262306a36Sopenharmony_ci n = (cfg_reg->reg_width / fw) << fw; 88362306a36Sopenharmony_ci for (i = 0, r = 0; i < n; i += 1 << fw) { 88462306a36Sopenharmony_ci if (is0s(&cfg_reg->enum_ids[i], 1 << fw)) 88562306a36Sopenharmony_ci r++; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if ((r << fw) * sizeof(u16) > cfg_reg->reg_width / fw) 88962306a36Sopenharmony_ci sh_pfc_warn("reg 0x%x can be described with variable-width reserved fields\n", 89062306a36Sopenharmony_ci cfg_reg->reg); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Skip field checks (done at build time) */ 89362306a36Sopenharmony_ci goto check_enum_ids; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci for (i = 0, n = 0, rw = 0; (fw = cfg_reg->var_field_width[i]); i++) { 89762306a36Sopenharmony_ci if (fw < 0) { 89862306a36Sopenharmony_ci rw += -fw; 89962306a36Sopenharmony_ci } else { 90062306a36Sopenharmony_ci if (is0s(&cfg_reg->enum_ids[n], 1 << fw)) 90162306a36Sopenharmony_ci sh_pfc_warn("reg 0x%x: field [%u:%u] can be described as reserved\n", 90262306a36Sopenharmony_ci cfg_reg->reg, rw, rw + fw - 1); 90362306a36Sopenharmony_ci n += 1 << fw; 90462306a36Sopenharmony_ci rw += fw; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (rw != cfg_reg->reg_width) 90962306a36Sopenharmony_ci sh_pfc_err("reg 0x%x: var_field_width declares %u instead of %u bits\n", 91062306a36Sopenharmony_ci cfg_reg->reg, rw, cfg_reg->reg_width); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (n != cfg_reg->nr_enum_ids) 91362306a36Sopenharmony_ci sh_pfc_err("reg 0x%x: enum_ids[] has %u instead of %u values\n", 91462306a36Sopenharmony_ci cfg_reg->reg, cfg_reg->nr_enum_ids, n); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cicheck_enum_ids: 91762306a36Sopenharmony_ci sh_pfc_check_reg_enums(drvname, cfg_reg->reg, cfg_reg->enum_ids, n); 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic void __init sh_pfc_check_drive_reg(const struct sh_pfc_soc_info *info, 92162306a36Sopenharmony_ci const struct pinmux_drive_reg *drive) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci const char *drvname = info->name; 92462306a36Sopenharmony_ci const struct sh_pfc_pin *pin; 92562306a36Sopenharmony_ci unsigned int i; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drive->fields); i++) { 92862306a36Sopenharmony_ci const struct pinmux_drive_reg_field *field = &drive->fields[i]; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (!field->pin && !field->offset && !field->size) 93162306a36Sopenharmony_ci continue; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci sh_pfc_check_reg(info->name, drive->reg, 93462306a36Sopenharmony_ci GENMASK(field->offset + field->size - 1, 93562306a36Sopenharmony_ci field->offset)); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci pin = sh_pfc_find_pin(info, drive->reg, field->pin); 93862306a36Sopenharmony_ci if (pin && !(pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH)) 93962306a36Sopenharmony_ci sh_pfc_err("drive_reg 0x%x: field %u: pin %s lacks SH_PFC_PIN_CFG_DRIVE_STRENGTH flag\n", 94062306a36Sopenharmony_ci drive->reg, i, pin->name); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic void __init sh_pfc_check_bias_reg(const struct sh_pfc_soc_info *info, 94562306a36Sopenharmony_ci const struct pinmux_bias_reg *bias) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci const char *drvname = info->name; 94862306a36Sopenharmony_ci const struct sh_pfc_pin *pin; 94962306a36Sopenharmony_ci unsigned int i; 95062306a36Sopenharmony_ci u32 bits; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci for (i = 0, bits = 0; i < ARRAY_SIZE(bias->pins); i++) 95362306a36Sopenharmony_ci if (bias->pins[i] != SH_PFC_PIN_NONE) 95462306a36Sopenharmony_ci bits |= BIT(i); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (bias->puen) 95762306a36Sopenharmony_ci sh_pfc_check_reg(info->name, bias->puen, bits); 95862306a36Sopenharmony_ci if (bias->pud) 95962306a36Sopenharmony_ci sh_pfc_check_reg(info->name, bias->pud, bits); 96062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bias->pins); i++) { 96162306a36Sopenharmony_ci pin = sh_pfc_find_pin(info, bias->puen, bias->pins[i]); 96262306a36Sopenharmony_ci if (!pin) 96362306a36Sopenharmony_ci continue; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (bias->puen && bias->pud) { 96662306a36Sopenharmony_ci /* 96762306a36Sopenharmony_ci * Pull-enable and pull-up/down control registers 96862306a36Sopenharmony_ci * As some SoCs have pins that support only pull-up 96962306a36Sopenharmony_ci * or pull-down, we just check for one of them 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci if (!(pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN)) 97262306a36Sopenharmony_ci sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks one or more SH_PFC_PIN_CFG_PULL_* flags\n", 97362306a36Sopenharmony_ci bias->puen, i, pin->name); 97462306a36Sopenharmony_ci } else if (bias->puen) { 97562306a36Sopenharmony_ci /* Pull-up control register only */ 97662306a36Sopenharmony_ci if (!(pin->configs & SH_PFC_PIN_CFG_PULL_UP)) 97762306a36Sopenharmony_ci sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks SH_PFC_PIN_CFG_PULL_UP flag\n", 97862306a36Sopenharmony_ci bias->puen, i, pin->name); 97962306a36Sopenharmony_ci } else if (bias->pud) { 98062306a36Sopenharmony_ci /* Pull-down control register only */ 98162306a36Sopenharmony_ci if (!(pin->configs & SH_PFC_PIN_CFG_PULL_DOWN)) 98262306a36Sopenharmony_ci sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks SH_PFC_PIN_CFG_PULL_DOWN flag\n", 98362306a36Sopenharmony_ci bias->pud, i, pin->name); 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic void __init sh_pfc_compare_groups(const char *drvname, 98962306a36Sopenharmony_ci const struct sh_pfc_pin_group *a, 99062306a36Sopenharmony_ci const struct sh_pfc_pin_group *b) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci unsigned int i; 99362306a36Sopenharmony_ci size_t len; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (same_name(a->name, b->name)) 99662306a36Sopenharmony_ci sh_pfc_err("group %s: name conflict\n", a->name); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (a->nr_pins > b->nr_pins) 99962306a36Sopenharmony_ci swap(a, b); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci len = a->nr_pins * sizeof(a->pins[0]); 100262306a36Sopenharmony_ci for (i = 0; i <= b->nr_pins - a->nr_pins; i++) { 100362306a36Sopenharmony_ci if (a->pins == b->pins + i || a->mux == b->mux + i || 100462306a36Sopenharmony_ci memcmp(a->pins, b->pins + i, len) || 100562306a36Sopenharmony_ci memcmp(a->mux, b->mux + i, len)) 100662306a36Sopenharmony_ci continue; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (a->nr_pins == b->nr_pins) 100962306a36Sopenharmony_ci sh_pfc_warn("group %s can be an alias for %s\n", 101062306a36Sopenharmony_ci a->name, b->name); 101162306a36Sopenharmony_ci else 101262306a36Sopenharmony_ci sh_pfc_warn("group %s is a subset of %s\n", a->name, 101362306a36Sopenharmony_ci b->name); 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic void __init sh_pfc_check_info(const struct sh_pfc_soc_info *info) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci const struct pinmux_drive_reg *drive_regs = info->drive_regs; 102062306a36Sopenharmony_ci#define drive_nfields ARRAY_SIZE(drive_regs->fields) 102162306a36Sopenharmony_ci#define drive_ofs(i) drive_regs[(i) / drive_nfields] 102262306a36Sopenharmony_ci#define drive_reg(i) drive_ofs(i).reg 102362306a36Sopenharmony_ci#define drive_bit(i) ((i) % drive_nfields) 102462306a36Sopenharmony_ci#define drive_field(i) drive_ofs(i).fields[drive_bit(i)] 102562306a36Sopenharmony_ci const struct pinmux_bias_reg *bias_regs = info->bias_regs; 102662306a36Sopenharmony_ci#define bias_npins ARRAY_SIZE(bias_regs->pins) 102762306a36Sopenharmony_ci#define bias_ofs(i) bias_regs[(i) / bias_npins] 102862306a36Sopenharmony_ci#define bias_puen(i) bias_ofs(i).puen 102962306a36Sopenharmony_ci#define bias_pud(i) bias_ofs(i).pud 103062306a36Sopenharmony_ci#define bias_bit(i) ((i) % bias_npins) 103162306a36Sopenharmony_ci#define bias_pin(i) bias_ofs(i).pins[bias_bit(i)] 103262306a36Sopenharmony_ci const char *drvname = info->name; 103362306a36Sopenharmony_ci unsigned int *refcnts; 103462306a36Sopenharmony_ci unsigned int i, j, k; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci pr_info("sh_pfc: Checking %s\n", drvname); 103762306a36Sopenharmony_ci sh_pfc_num_regs = 0; 103862306a36Sopenharmony_ci sh_pfc_num_enums = 0; 103962306a36Sopenharmony_ci sh_pfc_bias_done = false; 104062306a36Sopenharmony_ci sh_pfc_drive_done = false; 104162306a36Sopenharmony_ci sh_pfc_power_done = false; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* Check pins */ 104462306a36Sopenharmony_ci for (i = 0; i < info->nr_pins; i++) { 104562306a36Sopenharmony_ci const struct sh_pfc_pin *pin = &info->pins[i]; 104662306a36Sopenharmony_ci unsigned int x; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (!pin->name) { 104962306a36Sopenharmony_ci sh_pfc_err("empty pin %u\n", i); 105062306a36Sopenharmony_ci continue; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci for (j = 0; j < i; j++) { 105362306a36Sopenharmony_ci const struct sh_pfc_pin *pin2 = &info->pins[j]; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci if (same_name(pin->name, pin2->name)) 105662306a36Sopenharmony_ci sh_pfc_err("pin %s: name conflict\n", 105762306a36Sopenharmony_ci pin->name); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (pin->pin != (u16)-1 && pin->pin == pin2->pin) 106062306a36Sopenharmony_ci sh_pfc_err("pin %s/%s: pin %u conflict\n", 106162306a36Sopenharmony_ci pin->name, pin2->name, pin->pin); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (pin->enum_id && pin->enum_id == pin2->enum_id) 106462306a36Sopenharmony_ci sh_pfc_err("pin %s/%s: enum_id %u conflict\n", 106562306a36Sopenharmony_ci pin->name, pin2->name, 106662306a36Sopenharmony_ci pin->enum_id); 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN) { 107062306a36Sopenharmony_ci if (!info->ops || !info->ops->get_bias || 107162306a36Sopenharmony_ci !info->ops->set_bias) 107262306a36Sopenharmony_ci sh_pfc_err_once(bias, "SH_PFC_PIN_CFG_PULL_* flag set but .[gs]et_bias() not implemented\n"); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (!bias_regs && 107562306a36Sopenharmony_ci (!info->ops || !info->ops->pin_to_portcr)) 107662306a36Sopenharmony_ci sh_pfc_err_once(bias, "SH_PFC_PIN_CFG_PULL_UP flag set but no bias_regs defined and .pin_to_portcr() not implemented\n"); 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if ((pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN) && bias_regs) { 108062306a36Sopenharmony_ci const struct pinmux_bias_reg *bias_reg = 108162306a36Sopenharmony_ci rcar_pin_to_bias_reg(info, pin->pin, &x); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (!bias_reg || 108462306a36Sopenharmony_ci ((pin->configs & SH_PFC_PIN_CFG_PULL_UP) && 108562306a36Sopenharmony_ci !bias_reg->puen)) 108662306a36Sopenharmony_ci sh_pfc_err("pin %s: SH_PFC_PIN_CFG_PULL_UP flag set but pin not in bias_regs\n", 108762306a36Sopenharmony_ci pin->name); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (!bias_reg || 109062306a36Sopenharmony_ci ((pin->configs & SH_PFC_PIN_CFG_PULL_DOWN) && 109162306a36Sopenharmony_ci !bias_reg->pud)) 109262306a36Sopenharmony_ci sh_pfc_err("pin %s: SH_PFC_PIN_CFG_PULL_DOWN flag set but pin not in bias_regs\n", 109362306a36Sopenharmony_ci pin->name); 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci if (pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH) { 109762306a36Sopenharmony_ci if (!drive_regs) { 109862306a36Sopenharmony_ci sh_pfc_err_once(drive, "SH_PFC_PIN_CFG_DRIVE_STRENGTH flag set but drive_regs missing\n"); 109962306a36Sopenharmony_ci } else { 110062306a36Sopenharmony_ci for (j = 0; drive_reg(j); j++) { 110162306a36Sopenharmony_ci if (!drive_field(j).pin && 110262306a36Sopenharmony_ci !drive_field(j).offset && 110362306a36Sopenharmony_ci !drive_field(j).size) 110462306a36Sopenharmony_ci continue; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (drive_field(j).pin == pin->pin) 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (!drive_reg(j)) 111162306a36Sopenharmony_ci sh_pfc_err("pin %s: SH_PFC_PIN_CFG_DRIVE_STRENGTH flag set but not in drive_regs\n", 111262306a36Sopenharmony_ci pin->name); 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE_MASK) { 111762306a36Sopenharmony_ci if (!info->ops || !info->ops->pin_to_pocctrl) 111862306a36Sopenharmony_ci sh_pfc_err_once(power, "SH_PFC_PIN_CFG_IO_VOLTAGE set but .pin_to_pocctrl() not implemented\n"); 111962306a36Sopenharmony_ci else if (info->ops->pin_to_pocctrl(pin->pin, &x) < 0) 112062306a36Sopenharmony_ci sh_pfc_err("pin %s: SH_PFC_PIN_CFG_IO_VOLTAGE set but invalid pin_to_pocctrl()\n", 112162306a36Sopenharmony_ci pin->name); 112262306a36Sopenharmony_ci } else if (info->ops && info->ops->pin_to_pocctrl && 112362306a36Sopenharmony_ci info->ops->pin_to_pocctrl(pin->pin, &x) >= 0) { 112462306a36Sopenharmony_ci sh_pfc_warn("pin %s: SH_PFC_PIN_CFG_IO_VOLTAGE not set but valid pin_to_pocctrl()\n", 112562306a36Sopenharmony_ci pin->name); 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* Check groups and functions */ 113062306a36Sopenharmony_ci refcnts = kcalloc(info->nr_groups, sizeof(*refcnts), GFP_KERNEL); 113162306a36Sopenharmony_ci if (!refcnts) 113262306a36Sopenharmony_ci return; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci for (i = 0; i < info->nr_functions; i++) { 113562306a36Sopenharmony_ci const struct sh_pfc_function *func = &info->functions[i]; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (!func->name) { 113862306a36Sopenharmony_ci sh_pfc_err("empty function %u\n", i); 113962306a36Sopenharmony_ci continue; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci for (j = 0; j < i; j++) { 114262306a36Sopenharmony_ci if (same_name(func->name, info->functions[j].name)) 114362306a36Sopenharmony_ci sh_pfc_err("function %s: name conflict\n", 114462306a36Sopenharmony_ci func->name); 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci for (j = 0; j < func->nr_groups; j++) { 114762306a36Sopenharmony_ci for (k = 0; k < info->nr_groups; k++) { 114862306a36Sopenharmony_ci if (same_name(func->groups[j], 114962306a36Sopenharmony_ci info->groups[k].name)) { 115062306a36Sopenharmony_ci refcnts[k]++; 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (k == info->nr_groups) 115662306a36Sopenharmony_ci sh_pfc_err("function %s: group %s not found\n", 115762306a36Sopenharmony_ci func->name, func->groups[j]); 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci for (i = 0; i < info->nr_groups; i++) { 116262306a36Sopenharmony_ci const struct sh_pfc_pin_group *group = &info->groups[i]; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (!group->name) { 116562306a36Sopenharmony_ci sh_pfc_err("empty group %u\n", i); 116662306a36Sopenharmony_ci continue; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci for (j = 0; j < i; j++) 116962306a36Sopenharmony_ci sh_pfc_compare_groups(drvname, group, &info->groups[j]); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (!refcnts[i]) 117262306a36Sopenharmony_ci sh_pfc_err("orphan group %s\n", group->name); 117362306a36Sopenharmony_ci else if (refcnts[i] > 1) 117462306a36Sopenharmony_ci sh_pfc_warn("group %s referenced by %u functions\n", 117562306a36Sopenharmony_ci group->name, refcnts[i]); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci kfree(refcnts); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci /* Check config register descriptions */ 118162306a36Sopenharmony_ci for (i = 0; info->cfg_regs && info->cfg_regs[i].reg; i++) 118262306a36Sopenharmony_ci sh_pfc_check_cfg_reg(drvname, &info->cfg_regs[i]); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* Check drive strength registers */ 118562306a36Sopenharmony_ci for (i = 0; drive_regs && drive_regs[i].reg; i++) 118662306a36Sopenharmony_ci sh_pfc_check_drive_reg(info, &drive_regs[i]); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci for (i = 0; drive_regs && drive_reg(i); i++) { 118962306a36Sopenharmony_ci if (!drive_field(i).pin && !drive_field(i).offset && 119062306a36Sopenharmony_ci !drive_field(i).size) 119162306a36Sopenharmony_ci continue; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci for (j = 0; j < i; j++) { 119462306a36Sopenharmony_ci if (drive_field(i).pin == drive_field(j).pin && 119562306a36Sopenharmony_ci drive_field(j).offset && drive_field(j).size) { 119662306a36Sopenharmony_ci sh_pfc_err("drive_reg 0x%x:%zu/0x%x:%zu: pin conflict\n", 119762306a36Sopenharmony_ci drive_reg(i), drive_bit(i), 119862306a36Sopenharmony_ci drive_reg(j), drive_bit(j)); 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci /* Check bias registers */ 120462306a36Sopenharmony_ci for (i = 0; bias_regs && (bias_regs[i].puen || bias_regs[i].pud); i++) 120562306a36Sopenharmony_ci sh_pfc_check_bias_reg(info, &bias_regs[i]); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci for (i = 0; bias_regs && (bias_puen(i) || bias_pud(i)); i++) { 120862306a36Sopenharmony_ci if (bias_pin(i) == SH_PFC_PIN_NONE) 120962306a36Sopenharmony_ci continue; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci for (j = 0; j < i; j++) { 121262306a36Sopenharmony_ci if (bias_pin(i) != bias_pin(j)) 121362306a36Sopenharmony_ci continue; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (bias_puen(i) && bias_puen(j)) 121662306a36Sopenharmony_ci sh_pfc_err("bias_reg 0x%x:%zu/0x%x:%zu: pin conflict\n", 121762306a36Sopenharmony_ci bias_puen(i), bias_bit(i), 121862306a36Sopenharmony_ci bias_puen(j), bias_bit(j)); 121962306a36Sopenharmony_ci if (bias_pud(i) && bias_pud(j)) 122062306a36Sopenharmony_ci sh_pfc_err("bias_reg 0x%x:%zu/0x%x:%zu: pin conflict\n", 122162306a36Sopenharmony_ci bias_pud(i), bias_bit(i), 122262306a36Sopenharmony_ci bias_pud(j), bias_bit(j)); 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* Check ioctrl registers */ 122762306a36Sopenharmony_ci for (i = 0; info->ioctrl_regs && info->ioctrl_regs[i].reg; i++) 122862306a36Sopenharmony_ci sh_pfc_check_reg(drvname, info->ioctrl_regs[i].reg, U32_MAX); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* Check data registers */ 123162306a36Sopenharmony_ci for (i = 0; info->data_regs && info->data_regs[i].reg; i++) { 123262306a36Sopenharmony_ci sh_pfc_check_reg(drvname, info->data_regs[i].reg, 123362306a36Sopenharmony_ci GENMASK(info->data_regs[i].reg_width - 1, 0)); 123462306a36Sopenharmony_ci sh_pfc_check_reg_enums(drvname, info->data_regs[i].reg, 123562306a36Sopenharmony_ci info->data_regs[i].enum_ids, 123662306a36Sopenharmony_ci info->data_regs[i].reg_width); 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_SH_FUNC_GPIO 124062306a36Sopenharmony_ci /* Check function GPIOs */ 124162306a36Sopenharmony_ci for (i = 0; i < info->nr_func_gpios; i++) { 124262306a36Sopenharmony_ci const struct pinmux_func *func = &info->func_gpios[i]; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (!func->name) { 124562306a36Sopenharmony_ci sh_pfc_err("empty function gpio %u\n", i); 124662306a36Sopenharmony_ci continue; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci for (j = 0; j < i; j++) { 124962306a36Sopenharmony_ci if (same_name(func->name, info->func_gpios[j].name)) 125062306a36Sopenharmony_ci sh_pfc_err("func_gpio %s: name conflict\n", 125162306a36Sopenharmony_ci func->name); 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci if (sh_pfc_check_enum(drvname, func->enum_id)) 125462306a36Sopenharmony_ci sh_pfc_err("%s enum_id %u conflict\n", func->name, 125562306a36Sopenharmony_ci func->enum_id); 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci#endif 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic void __init sh_pfc_check_driver(const struct platform_driver *pdrv) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci unsigned int i; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SUPERH) && 126562306a36Sopenharmony_ci !of_find_matching_node(NULL, pdrv->driver.of_match_table)) 126662306a36Sopenharmony_ci return; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci sh_pfc_regs = kcalloc(SH_PFC_MAX_REGS, sizeof(*sh_pfc_regs), 126962306a36Sopenharmony_ci GFP_KERNEL); 127062306a36Sopenharmony_ci if (!sh_pfc_regs) 127162306a36Sopenharmony_ci return; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci sh_pfc_enums = kcalloc(SH_PFC_MAX_ENUMS, sizeof(*sh_pfc_enums), 127462306a36Sopenharmony_ci GFP_KERNEL); 127562306a36Sopenharmony_ci if (!sh_pfc_enums) 127662306a36Sopenharmony_ci goto free_regs; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci pr_warn("sh_pfc: Checking builtin pinmux tables\n"); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci for (i = 0; pdrv->id_table[i].name[0]; i++) 128162306a36Sopenharmony_ci sh_pfc_check_info((void *)pdrv->id_table[i].driver_data); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci#ifdef CONFIG_OF 128462306a36Sopenharmony_ci for (i = 0; pdrv->driver.of_match_table[i].compatible[0]; i++) 128562306a36Sopenharmony_ci sh_pfc_check_info(pdrv->driver.of_match_table[i].data); 128662306a36Sopenharmony_ci#endif 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci pr_warn("sh_pfc: Detected %u errors and %u warnings\n", sh_pfc_errors, 128962306a36Sopenharmony_ci sh_pfc_warnings); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci kfree(sh_pfc_enums); 129262306a36Sopenharmony_cifree_regs: 129362306a36Sopenharmony_ci kfree(sh_pfc_regs); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci#else /* !DEBUG */ 129762306a36Sopenharmony_cistatic inline void sh_pfc_check_driver(struct platform_driver *pdrv) {} 129862306a36Sopenharmony_ci#endif /* !DEBUG */ 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_cistatic int sh_pfc_probe(struct platform_device *pdev) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci const struct sh_pfc_soc_info *info; 130362306a36Sopenharmony_ci struct sh_pfc *pfc; 130462306a36Sopenharmony_ci int ret; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (pdev->dev.of_node) 130762306a36Sopenharmony_ci info = of_device_get_match_data(&pdev->dev); 130862306a36Sopenharmony_ci else 130962306a36Sopenharmony_ci info = (const void *)platform_get_device_id(pdev)->driver_data; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci pfc = devm_kzalloc(&pdev->dev, sizeof(*pfc), GFP_KERNEL); 131262306a36Sopenharmony_ci if (pfc == NULL) 131362306a36Sopenharmony_ci return -ENOMEM; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci pfc->info = info; 131662306a36Sopenharmony_ci pfc->dev = &pdev->dev; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci ret = sh_pfc_map_resources(pfc, pdev); 131962306a36Sopenharmony_ci if (unlikely(ret < 0)) 132062306a36Sopenharmony_ci return ret; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci spin_lock_init(&pfc->lock); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (info->ops && info->ops->init) { 132562306a36Sopenharmony_ci ret = info->ops->init(pfc); 132662306a36Sopenharmony_ci if (ret < 0) 132762306a36Sopenharmony_ci return ret; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci /* .init() may have overridden pfc->info */ 133062306a36Sopenharmony_ci info = pfc->info; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci ret = sh_pfc_suspend_init(pfc); 133462306a36Sopenharmony_ci if (ret) 133562306a36Sopenharmony_ci return ret; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* Enable dummy states for those platforms without pinctrl support */ 133862306a36Sopenharmony_ci if (!of_have_populated_dt()) 133962306a36Sopenharmony_ci pinctrl_provide_dummies(); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci ret = sh_pfc_init_ranges(pfc); 134262306a36Sopenharmony_ci if (ret < 0) 134362306a36Sopenharmony_ci return ret; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci /* 134662306a36Sopenharmony_ci * Initialize pinctrl bindings first 134762306a36Sopenharmony_ci */ 134862306a36Sopenharmony_ci ret = sh_pfc_register_pinctrl(pfc); 134962306a36Sopenharmony_ci if (unlikely(ret != 0)) 135062306a36Sopenharmony_ci return ret; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_SH_PFC_GPIO 135362306a36Sopenharmony_ci /* 135462306a36Sopenharmony_ci * Then the GPIO chip 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_ci ret = sh_pfc_register_gpiochip(pfc); 135762306a36Sopenharmony_ci if (unlikely(ret != 0)) { 135862306a36Sopenharmony_ci /* 135962306a36Sopenharmony_ci * If the GPIO chip fails to come up we still leave the 136062306a36Sopenharmony_ci * PFC state as it is, given that there are already 136162306a36Sopenharmony_ci * extant users of it that have succeeded by this point. 136262306a36Sopenharmony_ci */ 136362306a36Sopenharmony_ci dev_notice(pfc->dev, "failed to init GPIO chip, ignoring...\n"); 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci#endif 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci platform_set_drvdata(pdev, pfc); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci dev_info(pfc->dev, "%s support registered\n", info->name); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci return 0; 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_cistatic const struct platform_device_id sh_pfc_id_table[] = { 137562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7203 137662306a36Sopenharmony_ci { "pfc-sh7203", (kernel_ulong_t)&sh7203_pinmux_info }, 137762306a36Sopenharmony_ci#endif 137862306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7264 137962306a36Sopenharmony_ci { "pfc-sh7264", (kernel_ulong_t)&sh7264_pinmux_info }, 138062306a36Sopenharmony_ci#endif 138162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7269 138262306a36Sopenharmony_ci { "pfc-sh7269", (kernel_ulong_t)&sh7269_pinmux_info }, 138362306a36Sopenharmony_ci#endif 138462306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7720 138562306a36Sopenharmony_ci { "pfc-sh7720", (kernel_ulong_t)&sh7720_pinmux_info }, 138662306a36Sopenharmony_ci#endif 138762306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7722 138862306a36Sopenharmony_ci { "pfc-sh7722", (kernel_ulong_t)&sh7722_pinmux_info }, 138962306a36Sopenharmony_ci#endif 139062306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7723 139162306a36Sopenharmony_ci { "pfc-sh7723", (kernel_ulong_t)&sh7723_pinmux_info }, 139262306a36Sopenharmony_ci#endif 139362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7724 139462306a36Sopenharmony_ci { "pfc-sh7724", (kernel_ulong_t)&sh7724_pinmux_info }, 139562306a36Sopenharmony_ci#endif 139662306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7734 139762306a36Sopenharmony_ci { "pfc-sh7734", (kernel_ulong_t)&sh7734_pinmux_info }, 139862306a36Sopenharmony_ci#endif 139962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7757 140062306a36Sopenharmony_ci { "pfc-sh7757", (kernel_ulong_t)&sh7757_pinmux_info }, 140162306a36Sopenharmony_ci#endif 140262306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7785 140362306a36Sopenharmony_ci { "pfc-sh7785", (kernel_ulong_t)&sh7785_pinmux_info }, 140462306a36Sopenharmony_ci#endif 140562306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SH7786 140662306a36Sopenharmony_ci { "pfc-sh7786", (kernel_ulong_t)&sh7786_pinmux_info }, 140762306a36Sopenharmony_ci#endif 140862306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_PFC_SHX3 140962306a36Sopenharmony_ci { "pfc-shx3", (kernel_ulong_t)&shx3_pinmux_info }, 141062306a36Sopenharmony_ci#endif 141162306a36Sopenharmony_ci { /* sentinel */ } 141262306a36Sopenharmony_ci}; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cistatic struct platform_driver sh_pfc_driver = { 141562306a36Sopenharmony_ci .probe = sh_pfc_probe, 141662306a36Sopenharmony_ci .id_table = sh_pfc_id_table, 141762306a36Sopenharmony_ci .driver = { 141862306a36Sopenharmony_ci .name = DRV_NAME, 141962306a36Sopenharmony_ci .of_match_table = of_match_ptr(sh_pfc_of_table), 142062306a36Sopenharmony_ci .pm = pm_psci_sleep_ptr(&sh_pfc_pm), 142162306a36Sopenharmony_ci }, 142262306a36Sopenharmony_ci}; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic int __init sh_pfc_init(void) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci sh_pfc_check_driver(&sh_pfc_driver); 142762306a36Sopenharmony_ci return platform_driver_register(&sh_pfc_driver); 142862306a36Sopenharmony_ci} 142962306a36Sopenharmony_cipostcore_initcall(sh_pfc_init); 1430