Merge patch series "Support Aspeed SGPIO controller"

Billy Tsai <billy_tsai@aspeedtech.com> says:

AST2600 SoC has 2 SGPIO master interfaces one with 128 pins another one
with 80 pins, AST2500/AST2400 SoC has 1 SGPIO master interface that
supports up to 80 pins.

Link: https://lore.kernel.org/r/20241016085955.314236-1-billy_tsai@aspeedtech.com
This commit is contained in:
Tom Rini
2024-10-29 12:12:09 -06:00
9 changed files with 404 additions and 0 deletions

View File

@@ -129,3 +129,8 @@
reg = <0x4d>;
};
};
&sgpio {
status = "okay";
ngpios = <80>;
};

View File

@@ -255,6 +255,21 @@
interrupt-controller;
};
sgpio: sgpio@1e780200 {
compatible = "aspeed,ast2500-sgpio";
reg = <0x1e780200 0x100>;
interrupts = <40>;
clocks = <&scu ASPEED_CLK_APB>;
#gpio-cells = <2>;
gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
bus-frequency = <1000000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sgpm_default>;
status = "disabled";
};
timer: timer@1e782000 {
/* This timer is a Faraday FTTMR010 derivative */
compatible = "aspeed,ast2400-timer";
@@ -1445,4 +1460,9 @@
function = "WDTRST2";
groups = "WDTRST2";
};
pinctrl_sgpm_default: sgpm_default {
function = "SGPM";
groups = "SGPM";
};
};

View File

@@ -266,3 +266,13 @@
bootph-all;
status = "okay";
};
&sgpiom0 {
status = "okay";
ngpios = <128>;
};
&sgpiom1 {
status = "okay";
ngpios = <80>;
};

View File

@@ -501,6 +501,36 @@
ngpios = <208>;
};
sgpiom0: sgpiom@1e780500 {
compatible = "aspeed,ast2600-sgpiom";
reg = <0x1e780500 0x100>;
interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scu ASPEED_CLK_APB2>;
#gpio-cells = <2>;
gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
bus-frequency = <1000000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sgpm1_default>;
status = "disabled";
};
sgpiom1: sgpiom@1e780600 {
compatible = "aspeed,ast2600-sgpiom";
reg = <0x1e780600 0x100>;
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scu ASPEED_CLK_APB2>;
#gpio-cells = <2>;
gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
bus-frequency = <12000000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sgpm2_default>;
status = "disabled";
};
gpio1: gpio@1e780800 {
compatible = "aspeed,ast2600-gpio";
reg = <0x1e780800 0x800>;
@@ -2167,4 +2197,14 @@
function = "PCIE1RC";
groups = "PCIE1RC";
};
pinctrl_sgpm1_default: sgpm1_default {
function = "SGPM1";
groups = "SGPM1";
};
pinctrl_sgpm2_default: sgpm2_default {
function = "SGPM2";
groups = "SGPM2";
};
};

View File

@@ -157,6 +157,13 @@ config ASPEED_GPIO
is found in the AST2400, AST2500 and AST2600 BMC SoCs and
provides access to over 200 GPIOs on each chip.
config ASPEED_SGPIO
bool "Aspeed SGPIO Driver"
help
Say yes here to support the Aspeed serial GPIO driver. The controller
is found in the AST2400, AST2500 and AST2600 BMC SoCs and
provides access to generate serial GPIO signal.
config ASPEED_G7_GPIO
bool "Aspeed G7 GPIO Driver"
help

View File

@@ -14,6 +14,7 @@ obj-$(CONFIG_$(XPL_)DM_PCA953X) += pca953x_gpio.o
obj-$(CONFIG_ASPEED_GPIO) += gpio-aspeed.o
obj-$(CONFIG_ASPEED_G7_GPIO) += gpio-aspeed-g7.o
obj-$(CONFIG_ASPEED_SGPIO) += gpio-aspeed-sgpio.o
obj-$(CONFIG_AT91_GPIO) += at91_gpio.o
obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o
obj-$(CONFIG_BCM6345_GPIO) += bcm6345_gpio.o

View File

@@ -0,0 +1,310 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) ASPEED Technology Inc.
* Billy Tsai <billy_tsai@aspeedtech.com>
*/
#include <asm/io.h>
#include <asm/gpio.h>
#include <config.h>
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <asm/io.h>
#include <linux/bug.h>
#include <linux/sizes.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#define ASPEED_SGPIO_CTRL 0x54
#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
#define ASPEED_SGPIO_ENABLE BIT(0)
#define ASPEED_SGPIO_PINS_SHIFT 6
struct aspeed_sgpio_priv {
void *base;
struct clk pclk;
const struct aspeed_sgpio_pdata *pdata;
};
struct aspeed_sgpio_pdata {
const u32 pin_mask;
const struct aspeed_sgpio_llops *llops;
};
struct aspeed_sgpio_bank {
u16 val_regs;
u16 rdata_reg;
u16 tolerance_regs;
const char names[4][3];
};
/*
* Note: The "value" register returns the input value when the GPIO is
* configured as an input.
*
* The "rdata" register returns the output value when the GPIO is
* configured as an output.
*/
static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
{
.val_regs = 0x0000,
.rdata_reg = 0x0070,
.tolerance_regs = 0x0018,
.names = { "A", "B", "C", "D" },
},
{
.val_regs = 0x001C,
.rdata_reg = 0x0074,
.tolerance_regs = 0x0034,
.names = { "E", "F", "G", "H" },
},
{
.val_regs = 0x0038,
.rdata_reg = 0x0078,
.tolerance_regs = 0x0050,
.names = { "I", "J", "K", "L" },
},
{
.val_regs = 0x0090,
.rdata_reg = 0x007C,
.tolerance_regs = 0x00A8,
.names = { "M", "N", "O", "P" },
},
};
enum aspeed_sgpio_reg {
reg_val,
reg_rdata,
reg_tolerance,
};
struct aspeed_sgpio_llops {
void (*reg_bit_set)(struct aspeed_sgpio_priv *gpio, unsigned int offset,
const enum aspeed_sgpio_reg reg, bool val);
bool (*reg_bit_get)(struct aspeed_sgpio_priv *gpio, unsigned int offset,
const enum aspeed_sgpio_reg reg);
};
#define GPIO_VAL_VALUE 0x00
static void __iomem *bank_reg(struct aspeed_sgpio_priv *gpio,
const struct aspeed_sgpio_bank *bank,
const enum aspeed_sgpio_reg reg)
{
switch (reg) {
case reg_val:
return gpio->base + bank->val_regs + GPIO_VAL_VALUE;
case reg_rdata:
return gpio->base + bank->rdata_reg;
case reg_tolerance:
return gpio->base + bank->tolerance_regs;
default:
/* acturally if code runs to here, it's an error case */
BUG();
}
}
#define GPIO_BANK(x) ((x) >> 6)
#define GPIO_OFFSET(x) ((x) & GENMASK(5, 0))
#define GPIO_BIT(x) BIT(GPIO_OFFSET(x) >> 1)
static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
{
unsigned int bank;
bank = GPIO_BANK(offset);
WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
return &aspeed_sgpio_banks[bank];
}
static bool aspeed_sgpio_is_input(unsigned int offset)
{
return !(offset % 2);
}
static int aspeed_sgpio_get_value(struct udevice *dev, unsigned int offset)
{
struct aspeed_sgpio_priv *gpio = dev_get_priv(dev);
enum aspeed_sgpio_reg reg;
reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata;
return gpio->pdata->llops->reg_bit_get(gpio, offset, reg);
}
static int aspeed_sgpio_set_value(struct udevice *dev, unsigned int offset,
int value)
{
struct aspeed_sgpio_priv *gpio = dev_get_priv(dev);
if (aspeed_sgpio_is_input(offset))
return -EINVAL;
gpio->pdata->llops->reg_bit_set(gpio, offset, reg_val, value);
return 0;
}
static int aspeed_sgpio_direction_input(struct udevice *dev,
unsigned int offset)
{
return aspeed_sgpio_is_input(offset) ? 0 : -EINVAL;
}
static int aspeed_sgpio_set_flags(struct udevice *dev, unsigned int offset, ulong flags)
{
int ret = -EOPNOTSUPP;
if (flags & GPIOD_IS_OUT) {
bool value = flags & GPIOD_IS_OUT_ACTIVE;
ret = aspeed_sgpio_set_value(dev, offset, value);
} else if (flags & GPIOD_IS_IN) {
ret = aspeed_sgpio_direction_input(dev, offset);
}
return ret;
}
static int aspeed_sgpio_get_function(struct udevice *dev, unsigned int offset)
{
return aspeed_sgpio_is_input(offset) ? GPIOF_INPUT : GPIOF_OUTPUT;
}
static void aspeed_g4_reg_bit_set(struct aspeed_sgpio_priv *gpio, unsigned int offset,
const enum aspeed_sgpio_reg reg, bool val)
{
const struct aspeed_sgpio_bank *bank = to_bank(offset);
void __iomem *addr = bank_reg(gpio, bank, reg);
u32 temp;
if (reg == reg_val)
/* Since this is an output, read the cached value from rdata, then update val. */
temp = readl(bank_reg(gpio, bank, reg_rdata));
else
temp = readl(addr);
if (val)
temp |= GPIO_BIT(offset);
else
temp &= ~GPIO_BIT(offset);
writel(temp, addr);
}
static bool aspeed_g4_reg_bit_get(struct aspeed_sgpio_priv *gpio, unsigned int offset,
const enum aspeed_sgpio_reg reg)
{
const struct aspeed_sgpio_bank *bank = to_bank(offset);
void __iomem *addr = bank_reg(gpio, bank, reg);
return !!(readl(addr) & GPIO_BIT(offset));
}
static const struct aspeed_sgpio_llops aspeed_g4_llops = {
.reg_bit_set = aspeed_g4_reg_bit_set,
.reg_bit_get = aspeed_g4_reg_bit_get,
};
static const struct dm_gpio_ops aspeed_sgpio_ops = {
.get_value = aspeed_sgpio_get_value,
.set_value = aspeed_sgpio_set_value,
.get_function = aspeed_sgpio_get_function,
.set_flags = aspeed_sgpio_set_flags,
};
static int aspeed_sgpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct aspeed_sgpio_priv *priv = dev_get_priv(dev);
u32 sgpio_freq, sgpio_clk_div, nr_gpios, gpio_cnt_regval, pin_mask;
ulong apb_freq;
int ret;
priv->base = devfdt_get_addr_ptr(dev);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->pdata = (const struct aspeed_sgpio_pdata *)dev_get_driver_data(dev);
if (!priv->pdata)
return -EINVAL;
pin_mask = priv->pdata->pin_mask;
ret = ofnode_read_u32(dev_ofnode(dev), "ngpios", &nr_gpios);
if (ret < 0) {
dev_err(dev, "Could not read ngpios property\n");
return -EINVAL;
} else if (nr_gpios % 8) {
dev_err(dev, "Number of GPIOs not multiple of 8: %d\n",
nr_gpios);
return -EINVAL;
}
ret = ofnode_read_u32(dev_ofnode(dev), "bus-frequency", &sgpio_freq);
if (ret < 0) {
dev_err(dev, "Could not read bus-frequency property\n");
return -EINVAL;
}
ret = clk_get_by_index(dev, 0, &priv->pclk);
if (ret < 0) {
dev_err(dev, "get clock failed\n");
return ret;
}
apb_freq = clk_get_rate(&priv->pclk);
/*
* From the datasheet,
* SGPIO period = 1/PCLK * 2 * (GPIO254[31:16] + 1)
* period = 2 * (GPIO254[31:16] + 1) / PCLK
* frequency = 1 / (2 * (GPIO254[31:16] + 1) / PCLK)
* frequency = PCLK / (2 * (GPIO254[31:16] + 1))
* frequency * 2 * (GPIO254[31:16] + 1) = PCLK
* GPIO254[31:16] = PCLK / (frequency * 2) - 1
*/
if (sgpio_freq == 0)
return -EINVAL;
sgpio_clk_div = (apb_freq / (sgpio_freq * 2)) - 1;
if (sgpio_clk_div > (1 << 16) - 1)
return -EINVAL;
gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask;
writel(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval |
ASPEED_SGPIO_ENABLE, priv->base + ASPEED_SGPIO_CTRL);
uc_priv->bank_name = dev->name;
uc_priv->gpio_count = nr_gpios * 2;
return 0;
}
static const struct aspeed_sgpio_pdata ast2400_sgpio_pdata = {
.pin_mask = GENMASK(9, 6),
.llops = &aspeed_g4_llops,
};
static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = {
.pin_mask = GENMASK(10, 6),
.llops = &aspeed_g4_llops,
};
static const struct udevice_id aspeed_sgpio_ids[] = {
{ .compatible = "aspeed,ast2400-sgpio", .data = (ulong)&ast2400_sgpio_pdata, },
{ .compatible = "aspeed,ast2500-sgpio", .data = (ulong)&ast2400_sgpio_pdata, },
{ .compatible = "aspeed,ast2600-sgpiom", .data = (ulong)&ast2600_sgpiom_pdata, },
};
U_BOOT_DRIVER(sgpio_aspeed) = {
.name = "sgpio-aspeed",
.id = UCLASS_GPIO,
.of_match = aspeed_sgpio_ids,
.ops = &aspeed_sgpio_ops,
.probe = aspeed_sgpio_probe,
.priv_auto = sizeof(struct aspeed_sgpio_priv),
};

View File

@@ -62,6 +62,7 @@ static const struct ast2500_group_config ast2500_groups[] = {
{ "SD2", 5, (1 << 1) },
{ "FWSPICS1", 3, (1 << 24) },
{ "SPI1CS1", 1, (1 << 15) },
{ "SGPM", 2, (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) },
};
static int ast2500_pinctrl_get_groups_count(struct udevice *dev)

View File

@@ -454,6 +454,14 @@ static struct aspeed_sig_desc pwm15g1[] = {
{0x41c, BIT(31), 0},
};
static struct aspeed_sig_desc sgpm1[] = {
{0x414, GENMASK(27, 24), 0},
};
static struct aspeed_sig_desc sgpm2[] = {
{0x6d0, GENMASK(7, 4), 0},
};
static const struct aspeed_group_config ast2600_groups[] = {
{ "MAC1LINK", ARRAY_SIZE(mac1_link), mac1_link },
{ "MAC2LINK", ARRAY_SIZE(mac2_link), mac2_link },
@@ -543,6 +551,8 @@ static const struct aspeed_group_config ast2600_groups[] = {
{ "PWM14G1", ARRAY_SIZE(pwm14g1), pwm14g1 },
{ "PWM15G0", ARRAY_SIZE(pwm15g0), pwm15g0 },
{ "PWM15G1", ARRAY_SIZE(pwm15g1), pwm15g1 },
{ "SGPM1", ARRAY_SIZE(sgpm1), sgpm1 },
{ "SGPM2", ARRAY_SIZE(sgpm2), sgpm2 },
};
static int ast2600_pinctrl_get_groups_count(struct udevice *dev)