// SPDX-License-Identifier: GPL-2.0-only
/*
* Spreadtrum pin controller driver
* Copyright (C) 2017 Spreadtrum - http://www.spreadtrum.com
*/
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include "../core.h"
#include "../pinmux.h"
#include "../pinconf.h"
#include "../pinctrl-utils.h"
#include "pinctrl-sprd.h"
#define PINCTRL_BIT_MASK(width) (~(~0UL << (width)))
#define PINCTRL_REG_OFFSET 0x20
#define PINCTRL_REG_MISC_OFFSET 0x4020
#define PINCTRL_REG_LEN 0x4
#define PIN_FUNC_MASK (BIT(4) | BIT(5))
#define PIN_FUNC_SEL_1 ~PIN_FUNC_MASK
#define PIN_FUNC_SEL_2 BIT(4)
#define PIN_FUNC_SEL_3 BIT(5)
#define PIN_FUNC_SEL_4 PIN_FUNC_MASK
#define AP_SLEEP_MODE BIT(13)
#define PUBCP_SLEEP_MODE BIT(14)
#define TGLDSP_SLEEP_MODE BIT(15)
#define AGDSP_SLEEP_MODE BIT(16)
#define CM4_SLEEP_MODE BIT(17)
#define SLEEP_MODE_MASK GENMASK(5, 0)
#define SLEEP_MODE_SHIFT 13
#define SLEEP_INPUT BIT(1)
#define SLEEP_INPUT_MASK 0x1
#define SLEEP_INPUT_SHIFT 1
#define SLEEP_OUTPUT BIT(0)
#define SLEEP_OUTPUT_MASK 0x1
#define SLEEP_OUTPUT_SHIFT 0
#define DRIVE_STRENGTH_MASK GENMASK(3, 0)
#define DRIVE_STRENGTH_SHIFT 19
#define SLEEP_PULL_DOWN BIT(2)
#define SLEEP_PULL_DOWN_MASK 0x1
#define SLEEP_PULL_DOWN_SHIFT 2
#define PULL_DOWN BIT(6)
#define PULL_DOWN_MASK 0x1
#define PULL_DOWN_SHIFT 6
#define SLEEP_PULL_UP BIT(3)
#define SLEEP_PULL_UP_MASK 0x1
#define SLEEP_PULL_UP_SHIFT 3
#define PULL_UP_4_7K (BIT(12) | BIT(7))
#define PULL_UP_20K BIT(7)
#define PULL_UP_MASK 0x21
#define PULL_UP_SHIFT 7
#define INPUT_SCHMITT BIT(11)
#define INPUT_SCHMITT_MASK 0x1
#define INPUT_SCHMITT_SHIFT 11
enum pin_sleep_mode {
AP_SLEEP = BIT(0),
PUBCP_SLEEP = BIT(1),
TGLDSP_SLEEP = BIT(2),
AGDSP_SLEEP = BIT(3),
CM4_SLEEP = BIT(4),
};
enum pin_func_sel {
PIN_FUNC_1,
PIN_FUNC_2,
PIN_FUNC_3,
PIN_FUNC_4,
PIN_FUNC_MAX,
};
/**
* struct sprd_pin: represent one pin's description
* @name: pin name
* @number: pin number
* @type: pin type, can be GLOBAL_CTRL_PIN/COMMON_PIN/MISC_PIN
* @reg: pin register address
* @bit_offset: bit offset in pin register
* @bit_width: bit width in pin register
*/
struct sprd_pin {
const char *name;
unsigned int number;
enum pin_type type;
unsigned long reg;
unsigned long bit_offset;
unsigned long bit_width;
};
/**
* struct sprd_pin_group: represent one group's description
* @name: group name
* @npins: pin numbers of this group
* @pins: pointer to pins array
*/
struct sprd_pin_group {
const char *name;
unsigned int npins;
unsigned int *pins;
};
/**
* struct sprd_pinctrl_soc_info: represent the SoC's pins description
* @groups: pointer to groups of pins
* @ngroups: group numbers of the whole SoC
* @pins: pointer to pins description
* @npins: pin numbers of the whole SoC
* @grp_names: pointer to group names array
*/
struct sprd_pinctrl_soc_info {
struct sprd_pin_group *groups;
unsigned int ngroups;
struct sprd_pin *pins;
unsigned int npins;
const char **grp_names;
};
/**
* struct sprd_pinctrl: represent the pin controller device
* @dev: pointer to the device structure
* @pctl: pointer to the pinctrl handle
* @base: base address of the controller
* @info: pointer to SoC's pins description information
*/
struct sprd_pinctrl {
struct device *dev;
struct pinctrl_dev *pctl;
void __iomem *base;
struct sprd_pinctrl_soc_info *info;
};
#define SPRD_PIN_CONFIG_CONTROL (PIN_CONFIG_END + 1)
#define SPRD_PIN_CONFIG_SLEEP_MODE (PIN_CONFIG_END + 2)
static int sprd_pinctrl_get_id_by_name(struct sprd_pinctrl *sprd_pctl,
const char *name)
{
struct sprd_pinctrl_soc_info *info = sprd_pctl->info;
int i;
for (i = 0; i < info->npins; i++) {
if (!strcmp(info->pins[i].name, name))
return info->pins[i].number;
}
return -ENODEV;
}
static struct sprd_pin *
sprd_pinctrl_get_pin_by_id(struct sprd_pinctrl *sprd_pctl, unsigned int id)
{
struct sprd_pinctrl_soc_info *info = sprd_pctl->info;
struct sprd_pin *pin = NULL;
int i;
for (i = 0; i < info->npins; i++) {
if (info->pins[i].number == id) {
pin = &info->pins[i];
break;
}
}
return pin;
}
static const struct sprd_pin_group *
sprd_pinctrl_find_group_by_name(struct sprd_pinctrl *sprd_pctl,
const char *name)
{
struct sprd_pinctrl_soc_info *info = sprd_pctl->info;
const struct sprd_pin_group *grp = NULL;
int i;
for (i = 0; i < info->ngroups; i++) {
if (!strcmp(info->groups[i].name, name)) {
grp = &info->groups[i];
break;
}
}
return grp;
}
static