caa4daa2ae
We use 'priv' for private data but often use 'platdata' for platform data. We can't really use 'pdata' since that is ambiguous (it could mean private or platform data). Rename some of the latter variables to end with 'plat' for consistency. Signed-off-by: Simon Glass <sjg@chromium.org>
345 lines
7.5 KiB
C
345 lines
7.5 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Atmel PIO4 device driver
|
|
*
|
|
* Copyright (C) 2015 Atmel Corporation
|
|
* Wenyou.Yang <wenyou.yang@atmel.com>
|
|
*/
|
|
#include <common.h>
|
|
#include <clk.h>
|
|
#include <dm.h>
|
|
#include <fdtdec.h>
|
|
#include <malloc.h>
|
|
#include <asm/arch/hardware.h>
|
|
#include <asm/gpio.h>
|
|
#include <linux/bitops.h>
|
|
#include <mach/gpio.h>
|
|
#include <mach/atmel_pio4.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
static struct atmel_pio4_port *atmel_pio4_port_base(u32 port)
|
|
{
|
|
struct atmel_pio4_port *base = NULL;
|
|
|
|
switch (port) {
|
|
case AT91_PIO_PORTA:
|
|
base = (struct atmel_pio4_port *)ATMEL_BASE_PIOA;
|
|
break;
|
|
case AT91_PIO_PORTB:
|
|
base = (struct atmel_pio4_port *)ATMEL_BASE_PIOB;
|
|
break;
|
|
case AT91_PIO_PORTC:
|
|
base = (struct atmel_pio4_port *)ATMEL_BASE_PIOC;
|
|
break;
|
|
case AT91_PIO_PORTD:
|
|
base = (struct atmel_pio4_port *)ATMEL_BASE_PIOD;
|
|
break;
|
|
default:
|
|
printf("Error: Atmel PIO4: Failed to get PIO base of port#%d!\n",
|
|
port);
|
|
break;
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
static int atmel_pio4_config_io_func(u32 port, u32 pin,
|
|
u32 func, u32 config)
|
|
{
|
|
struct atmel_pio4_port *port_base;
|
|
u32 reg, mask;
|
|
|
|
if (pin >= ATMEL_PIO_NPINS_PER_BANK)
|
|
return -EINVAL;
|
|
|
|
port_base = atmel_pio4_port_base(port);
|
|
if (!port_base)
|
|
return -EINVAL;
|
|
|
|
mask = 1 << pin;
|
|
reg = func;
|
|
reg |= config;
|
|
|
|
writel(mask, &port_base->mskr);
|
|
writel(reg, &port_base->cfgr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int atmel_pio4_set_gpio(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_GPIO,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_a_periph(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_PERIPH_A,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_b_periph(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_PERIPH_B,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_c_periph(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_PERIPH_C,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_d_periph(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_PERIPH_D,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_e_periph(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_PERIPH_E,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_f_periph(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_PERIPH_F,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_g_periph(u32 port, u32 pin, u32 config)
|
|
{
|
|
return atmel_pio4_config_io_func(port, pin,
|
|
ATMEL_PIO_CFGR_FUNC_PERIPH_G,
|
|
config);
|
|
}
|
|
|
|
int atmel_pio4_set_pio_output(u32 port, u32 pin, u32 value)
|
|
{
|
|
struct atmel_pio4_port *port_base;
|
|
u32 reg, mask;
|
|
|
|
if (pin >= ATMEL_PIO_NPINS_PER_BANK)
|
|
return -EINVAL;
|
|
|
|
port_base = atmel_pio4_port_base(port);
|
|
if (!port_base)
|
|
return -EINVAL;
|
|
|
|
mask = 0x01 << pin;
|
|
reg = ATMEL_PIO_CFGR_FUNC_GPIO | ATMEL_PIO_DIR_MASK;
|
|
|
|
writel(mask, &port_base->mskr);
|
|
writel(reg, &port_base->cfgr);
|
|
|
|
if (value)
|
|
writel(mask, &port_base->sodr);
|
|
else
|
|
writel(mask, &port_base->codr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int atmel_pio4_get_pio_input(u32 port, u32 pin)
|
|
{
|
|
struct atmel_pio4_port *port_base;
|
|
u32 reg, mask;
|
|
|
|
if (pin >= ATMEL_PIO_NPINS_PER_BANK)
|
|
return -EINVAL;
|
|
|
|
port_base = atmel_pio4_port_base(port);
|
|
if (!port_base)
|
|
return -EINVAL;
|
|
|
|
mask = 0x01 << pin;
|
|
reg = ATMEL_PIO_CFGR_FUNC_GPIO;
|
|
|
|
writel(mask, &port_base->mskr);
|
|
writel(reg, &port_base->cfgr);
|
|
|
|
return (readl(&port_base->pdsr) & mask) ? 1 : 0;
|
|
}
|
|
|
|
#if CONFIG_IS_ENABLED(DM_GPIO)
|
|
|
|
struct atmel_pioctrl_data {
|
|
u32 nbanks;
|
|
};
|
|
|
|
struct atmel_pio4_platdata {
|
|
struct atmel_pio4_port *reg_base;
|
|
};
|
|
|
|
static struct atmel_pio4_port *atmel_pio4_bank_base(struct udevice *dev,
|
|
u32 bank)
|
|
{
|
|
struct atmel_pio4_platdata *plat = dev_get_platdata(dev);
|
|
struct atmel_pio4_port *port_base =
|
|
(struct atmel_pio4_port *)((u32)plat->reg_base +
|
|
ATMEL_PIO_BANK_OFFSET * bank);
|
|
|
|
return port_base;
|
|
}
|
|
|
|
static int atmel_pio4_direction_input(struct udevice *dev, unsigned offset)
|
|
{
|
|
u32 bank = ATMEL_PIO_BANK(offset);
|
|
u32 line = ATMEL_PIO_LINE(offset);
|
|
struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
|
|
u32 mask = BIT(line);
|
|
|
|
writel(mask, &port_base->mskr);
|
|
|
|
clrbits_le32(&port_base->cfgr,
|
|
ATMEL_PIO_CFGR_FUNC_MASK | ATMEL_PIO_DIR_MASK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atmel_pio4_direction_output(struct udevice *dev,
|
|
unsigned offset, int value)
|
|
{
|
|
u32 bank = ATMEL_PIO_BANK(offset);
|
|
u32 line = ATMEL_PIO_LINE(offset);
|
|
struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
|
|
u32 mask = BIT(line);
|
|
|
|
writel(mask, &port_base->mskr);
|
|
|
|
clrsetbits_le32(&port_base->cfgr,
|
|
ATMEL_PIO_CFGR_FUNC_MASK, ATMEL_PIO_DIR_MASK);
|
|
|
|
if (value)
|
|
writel(mask, &port_base->sodr);
|
|
else
|
|
writel(mask, &port_base->codr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atmel_pio4_get_value(struct udevice *dev, unsigned offset)
|
|
{
|
|
u32 bank = ATMEL_PIO_BANK(offset);
|
|
u32 line = ATMEL_PIO_LINE(offset);
|
|
struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
|
|
u32 mask = BIT(line);
|
|
|
|
return (readl(&port_base->pdsr) & mask) ? 1 : 0;
|
|
}
|
|
|
|
static int atmel_pio4_set_value(struct udevice *dev,
|
|
unsigned offset, int value)
|
|
{
|
|
u32 bank = ATMEL_PIO_BANK(offset);
|
|
u32 line = ATMEL_PIO_LINE(offset);
|
|
struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
|
|
u32 mask = BIT(line);
|
|
|
|
if (value)
|
|
writel(mask, &port_base->sodr);
|
|
else
|
|
writel(mask, &port_base->codr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atmel_pio4_get_function(struct udevice *dev, unsigned offset)
|
|
{
|
|
u32 bank = ATMEL_PIO_BANK(offset);
|
|
u32 line = ATMEL_PIO_LINE(offset);
|
|
struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
|
|
u32 mask = BIT(line);
|
|
|
|
writel(mask, &port_base->mskr);
|
|
|
|
return (readl(&port_base->cfgr) &
|
|
ATMEL_PIO_DIR_MASK) ? GPIOF_OUTPUT : GPIOF_INPUT;
|
|
}
|
|
|
|
static const struct dm_gpio_ops atmel_pio4_ops = {
|
|
.direction_input = atmel_pio4_direction_input,
|
|
.direction_output = atmel_pio4_direction_output,
|
|
.get_value = atmel_pio4_get_value,
|
|
.set_value = atmel_pio4_set_value,
|
|
.get_function = atmel_pio4_get_function,
|
|
};
|
|
|
|
static int atmel_pio4_bind(struct udevice *dev)
|
|
{
|
|
return dm_scan_fdt_dev(dev);
|
|
}
|
|
|
|
static int atmel_pio4_probe(struct udevice *dev)
|
|
{
|
|
struct atmel_pio4_platdata *plat = dev_get_platdata(dev);
|
|
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
struct atmel_pioctrl_data *pioctrl_data;
|
|
struct clk clk;
|
|
fdt_addr_t addr_base;
|
|
u32 nbanks;
|
|
int ret;
|
|
|
|
ret = clk_get_by_index(dev, 0, &clk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = clk_enable(&clk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
clk_free(&clk);
|
|
|
|
addr_base = dev_read_addr(dev);
|
|
if (addr_base == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
plat->reg_base = (struct atmel_pio4_port *)addr_base;
|
|
|
|
pioctrl_data = (struct atmel_pioctrl_data *)dev_get_driver_data(dev);
|
|
nbanks = pioctrl_data->nbanks;
|
|
|
|
uc_priv->bank_name = fdt_get_name(gd->fdt_blob, dev_of_offset(dev),
|
|
NULL);
|
|
uc_priv->gpio_count = nbanks * ATMEL_PIO_NPINS_PER_BANK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The number of banks can be different from a SoC to another one.
|
|
* We can have up to 16 banks.
|
|
*/
|
|
static const struct atmel_pioctrl_data atmel_sama5d2_pioctrl_data = {
|
|
.nbanks = 4,
|
|
};
|
|
|
|
static const struct udevice_id atmel_pio4_ids[] = {
|
|
{
|
|
.compatible = "atmel,sama5d2-gpio",
|
|
.data = (ulong)&atmel_sama5d2_pioctrl_data,
|
|
},
|
|
{}
|
|
};
|
|
|
|
U_BOOT_DRIVER(gpio_atmel_pio4) = {
|
|
.name = "gpio_atmel_pio4",
|
|
.id = UCLASS_GPIO,
|
|
.ops = &atmel_pio4_ops,
|
|
.probe = atmel_pio4_probe,
|
|
.bind = atmel_pio4_bind,
|
|
.of_match = atmel_pio4_ids,
|
|
.plat_auto = sizeof(struct atmel_pio4_platdata),
|
|
};
|
|
|
|
#endif
|