blob: 2d7302a2599449d91c3be6d32eca9ad2c53e3f01 [file] [log] [blame]
/*
* drivers/amlogic/pinctrl/pinconf-meson-g12a.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/init.h>
#include "pinctrl-meson.h"
#include "pinconf-meson-g12a.h"
static int meson_get_drive_bank(struct meson_pinctrl *pc, unsigned int pin,
struct meson_drive_bank **bank)
{
int i;
struct meson_drive_data *drive = pc->data->pcf_drv_data;
for (i = 0; i < drive->num_drive_banks; i++) {
if (pin >= drive->drive_banks[i].first &&
pin <= drive->drive_banks[i].last) {
*bank = &drive->drive_banks[i];
return 0;
}
}
return -EINVAL;
}
static void meson_drive_calc_reg_and_bit(struct meson_drive_bank *drive_bank,
unsigned int pin, unsigned int *reg,
unsigned int *bit)
{
struct meson_reg_desc *desc = &drive_bank->reg;
int shift = pin - drive_bank->first;
*reg = (desc->reg + (desc->bit + (shift << 1)) / 32) * 4;
*bit = (desc->bit + (shift << 1)) % 32;
}
static int meson_pinconf_set_drive_strength(struct meson_pinctrl *pc,
unsigned int pin, u16 arg)
{
struct meson_drive_bank *drive_bank;
unsigned int reg, bit;
int ret;
dev_dbg(pc->dev, "pin %u: set drive-strength\n", pin);
ret = meson_get_drive_bank(pc, pin, &drive_bank);
if (ret)
return ret;
if (arg >= 4) {
dev_err(pc->dev,
"pin %u: invalid drive-strength [0-3]: %d\n",
pin, arg);
return -EINVAL;
}
meson_drive_calc_reg_and_bit(drive_bank, pin,
&reg, &bit);
ret = regmap_update_bits(pc->reg_drive, reg,
0x3 << bit, (arg & 0x3) << bit);
if (ret)
return ret;
return 0;
}
static int meson_pinconf_get_drive_strength(struct meson_pinctrl *pc,
unsigned int pin, u16 *arg)
{
struct meson_drive_bank *drive_bank;
unsigned int reg, bit;
unsigned int val;
int ret;
ret = meson_get_drive_bank(pc, pin, &drive_bank);
if (ret)
return ret;
meson_drive_calc_reg_and_bit(drive_bank, pin, &reg, &bit);
ret = regmap_read(pc->reg_drive, reg, &val);
if (ret)
return ret;
*arg = (val >> bit) & 0x3;
return 0;
}
static int meson_g12a_pinconf_set(struct pinctrl_dev *pcdev, unsigned int pin,
unsigned long *configs, unsigned int num_configs)
{
struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
enum pin_config_param param;
int i, ret;
u16 arg;
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
if (param == PIN_CONFIG_DRIVE_STRENGTH) {
ret = meson_pinconf_set_drive_strength(pc, pin, arg);
if (ret)
return ret;
} else {
ret = meson_pinconf_common_set(pc, pin, param, arg);
if (ret)
return ret;
}
}
return 0;
}
static int meson_g12a_pinconf_get(struct pinctrl_dev *pcdev, unsigned int pin,
unsigned long *config)
{
struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
enum pin_config_param param = pinconf_to_config_param(*config);
int ret;
u16 arg;
if (param == PIN_CONFIG_DRIVE_STRENGTH) {
ret = meson_pinconf_get_drive_strength(pc, pin, &arg);
if (ret)
return ret;
} else {
ret = meson_pinconf_common_get(pc, pin, param, &arg);
if (ret)
return ret;
}
*config = pinconf_to_config_packed(param, arg);
dev_dbg(pc->dev, "pinconf for pin %u is %lu\n", pin, *config);
return 0;
}
static int meson_g12a_pinconf_group_set(struct pinctrl_dev *pcdev,
unsigned int num_group, unsigned long *configs,
unsigned int num_configs)
{
struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
struct meson_pmx_group *group = &pc->data->groups[num_group];
int i;
dev_dbg(pc->dev, "set pinconf for group %s\n", group->name);
for (i = 0; i < group->num_pins; i++) {
meson_g12a_pinconf_set(pcdev, group->pins[i], configs,
num_configs);
}
return 0;
}
const struct pinconf_ops meson_g12a_pinconf_ops = {
.pin_config_get = meson_g12a_pinconf_get,
.pin_config_set = meson_g12a_pinconf_set,
.pin_config_group_set = meson_g12a_pinconf_group_set,
.is_generic = true,
};