blob: 22d24a3eebc1520c107378da4048f441827a9557 [file] [log] [blame] [edit]
/*
* Copyright (C) 2016 IBM Corp.
*
* 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 version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/io.h>
#include <linux/platform_device.h>
#include "core.h"
#include "pinctrl-aspeed.h"
int aspeed_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
return pdata->ngroups;
}
const char *aspeed_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
unsigned group)
{
struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
return pdata->groups[group].name;
}
int aspeed_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
unsigned group, const unsigned **pins, unsigned *npins)
{
struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
*pins = &pdata->groups[group].pins[0];
*npins = pdata->groups[group].npins;
return 0;
}
void aspeed_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned offset)
{
seq_printf(s, " %s", dev_name(pctldev->dev));
}
int aspeed_pinmux_get_fn_count(struct pinctrl_dev *pctldev)
{
struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
return pdata->nfunctions;
}
const char *aspeed_pinmux_get_fn_name(struct pinctrl_dev *pctldev,
unsigned function)
{
struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
return pdata->functions[function].name;
}
int aspeed_pinmux_get_fn_groups(struct pinctrl_dev *pctldev,
unsigned function, const char * const **groups,
unsigned * const num_groups)
{
struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
*groups = pdata->functions[function].groups;
*num_groups = pdata->functions[function].ngroups;
return 0;
}
bool aspeed_func_desc_eval(const struct aspeed_func_desc *desc,
void __iomem *base)
{
u32 raw = ioread32(base + desc->reg);
return ((raw & desc->mask) >> __ffs(desc->mask)) == desc->enable;
}
bool aspeed_func_expr_eval(const struct aspeed_func_expr *expr, void __iomem *base)
{
bool ret = true;
int i;
for (i = 0; i < expr->ndescs; i++) {
const struct aspeed_func_desc *desc = &expr->descs[i];
ret = ret && aspeed_func_desc_eval(desc, base);
}
return ret;
}
bool aspeed_func_expr_enable(const struct aspeed_func_expr *expr, void __iomem *base)
{
int i;
int ret;
ret = aspeed_func_expr_eval(expr, base);
if (ret)
return ret;
for (i = 0; i < expr->ndescs; i++) {
const struct aspeed_func_desc *desc = &expr->descs[i];
u32 val;
/* FIXME: this is ast2400 specific */
if (desc->reg == STRAP)
continue;
val = ioread32(base + desc->reg);
val &= ~desc->mask;
val |= desc->enable << __ffs(desc->mask);
iowrite32(val, base + desc->reg);
}
ret = aspeed_func_expr_eval(expr, base);
return ret;
}
bool aspeed_func_expr_disable(const struct aspeed_func_expr *expr, void __iomem *base)
{
int i;
bool ret;
/* Ensure that we change any unsupported state to disabled */
for (i = 0; i < expr->ndescs; i++) {
const struct aspeed_func_desc *desc = &expr->descs[i];
u32 val;
if (desc->reg == STRAP)
continue;
val = ioread32(base + desc->reg);
val &= ~desc->mask;
val |= (desc->disable << __ffs(desc->mask));
iowrite32(val, base + desc->reg);
}
ret = !aspeed_func_expr_eval(expr, base);
return ret;
}
bool aspeed_maybe_disable(const struct aspeed_func_expr **expr, void __iomem *base)
{
bool ret;
if (!expr)
return true;
while (*expr) {
ret |= aspeed_func_expr_disable(*expr, base);
expr++;
}
return ret;
}
bool aspeed_sig_in_exprs(const struct aspeed_func_expr **exprs,
const struct aspeed_func_expr *sig)
{
if (!exprs)
return false;
while (*exprs) {
if (sig == *exprs)
return true;
exprs++;
}
return false;
}
int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned function,
unsigned group)
{
int i;
const struct aspeed_pinctrl_data *pdata =
pinctrl_dev_get_drvdata(pctldev);
const struct aspeed_pin_group *pgroup = &pdata->groups[group];
const struct aspeed_pin_function *pfunc =
&pdata->functions[function];
for (i = 0; i < pgroup->npins; i++) {
int pin = pgroup->pins[i];
const struct aspeed_pin_prio *pprio = pdata->pins[pin].drv_data;
const struct aspeed_func_expr *func = pfunc->expr;
if (!pprio)
return -1;
/* If low priority is requested, ensure high is disabled */
if (aspeed_sig_in_exprs(pprio->low, func)) {
if (!aspeed_maybe_disable(pprio->high, pdata->reg_base)) {
pr_err("Failed to disable high priority function on pin %d\n",
pin);
return -1;
}
}
/* Nothing to do if we're already enabled */
if (aspeed_func_expr_eval(func, pdata->reg_base))
continue;
/* Configure the pin */
if (!aspeed_func_expr_enable(func, pdata->reg_base))
return -1;
}
return 0;
}
int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset)
{
unsigned pin = offset;
const struct aspeed_pinctrl_data *pdata =
pinctrl_dev_get_drvdata(pctldev);
const struct aspeed_pin_prio *pprio =
pdata->pins[pin].drv_data;
if (!pprio)
return 0;
if (!aspeed_maybe_disable(pprio->high, pdata->reg_base))
return -1;
if (!aspeed_maybe_disable(pprio->low, pdata->reg_base))
return -1;
return 0;
}
int aspeed_pin_config_get(struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *config)
{
return 0;
}
int aspeed_pin_config_set(struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *configs,
unsigned num_configs)
{
return 0;
}