// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Core driver for the S32 CC (Common Chassis) pin controller
*
* Copyright 2017-2022,2024 NXP
* Copyright (C) 2022 SUSE LLC
* Copyright 2015-2016 Freescale Semiconductor, Inc.
*/
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include "../core.h"
#include "../pinconf.h"
#include "../pinctrl-utils.h"
#include "pinctrl-s32.h"
#define S32_PIN_ID_SHIFT 4
#define S32_PIN_ID_MASK GENMASK(31, S32_PIN_ID_SHIFT)
#define S32_MSCR_SSS_MASK GENMASK(2, 0)
#define S32_MSCR_PUS BIT(12)
#define S32_MSCR_PUE BIT(13)
#define S32_MSCR_SRE(X) (((X) & GENMASK(3, 0)) << 14)
#define S32_MSCR_IBE BIT(19)
#define S32_MSCR_ODE BIT(20)
#define S32_MSCR_OBE BIT(21)
enum s32_write_type {
S32_PINCONF_UPDATE_ONLY,
S32_PINCONF_OVERWRITE,
};
static struct regmap_config s32_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static u32 get_pin_no(u32 pinmux)
{
return (pinmux & S32_PIN_ID_MASK) >> S32_PIN_ID_SHIFT;
}
static u32 get_pin_func(u32 pinmux)
{
return pinmux & GENMASK(3, 0);
}
struct s32_pinctrl_mem_region {
struct regmap *map;
const struct s32_pin_range *pin_range;
char name[8];
};
/*
* Holds pin configuration for GPIO's.
* @pin_id: Pin ID for this GPIO
* @config: Pin settings
* @list: Linked list entry for each gpio pin
*/
struct gpio_pin_config {
unsigned int pin_id;
unsigned int config;
struct list_head list;
};
/*
* Pad config save/restore for power suspend/resume.
*/
struct s32_pinctrl_context {
unsigned int *pads;
};
/*
* @dev: a pointer back to containing device
* @pctl: a pointer to the pinctrl device structure
* @regions: reserved memory regions with start/end pin
* @info: structure containing information about the pin
* @gpio_configs: Saved configurations for GPIO pins
* @gpiop_configs_lock: lock for the `gpio_configs` list
* @s32_pinctrl_context: Configuration saved over system sleep
*/
struct s32_pinctrl {
struct device *dev;
struct pinctrl_dev *pctl;
struct s32_pinctrl_mem_region *regions;
struct s32_pinctrl_soc_info *info;
struct list_head gpio_configs;
spinlock_t gpio_configs_lock;
#ifdef CONFIG_PM_SLEEP
struct s32_pinctrl_context saved_context;
#endif
};
static struct s32_pinctrl_mem_region *
s32_get_region(struct pinctrl_dev *pctldev, unsigned int pin)
{
struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
const struct s32_pin_range *pin_range;
unsigned int mem_regions = ipctl->info->soc_data->mem_regions;
unsigned int i;
for (i = 0; i < mem_regions; i++) {
pin_range = ipctl->regions[i].pin_range;
if (pin >= pin_range->start && pin <= pin_range->end)
return &ipctl->regions[i];
}
return NULL;
}
static inline int s32_check_pin(struct pinctrl_dev *pctldev,
unsigned int pin)
{
return s32_get_region(pctldev, pin) ? 0 : -EINVAL;
}
static inline int s32_regmap_read(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned int *val)
{
struct s32_pinctrl_mem_region *region;
unsigned int offset;
region = s32_get_region(pctldev, pin);
if (!region)
return -EINVAL;
offset = (pin - region->pin_range->start) *
regmap_get_reg_stride(region->map);
return regmap_read(region->map, offset, val);
}
static inline int s32_regmap_write(struct pinctrl_dev *pctldev,
unsigned int pin,
unsigned int val)
{
struct s32_pinctrl_mem_region *region;
unsigned int offset;
region = s32_get_region(pctldev, pin);
if (!region)
return -EINVAL;
offset = (pin - region->pin_range->start) *
regmap_get_reg_stride(region->map);
return regmap_write(region->map, offset, val);
}
static inline int s32_regmap_update(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned int mask, unsigned int val)
{
struct s32_pinctrl_mem_region *regio