|
|
|
@ -15,6 +15,7 @@ |
|
|
|
|
#include <dm/lists.h> |
|
|
|
|
#include <dm/root.h> |
|
|
|
|
#include <errno.h> |
|
|
|
|
#include <reset.h> |
|
|
|
|
|
|
|
|
|
DECLARE_GLOBAL_DATA_PTR; |
|
|
|
|
|
|
|
|
@ -29,6 +30,10 @@ DECLARE_GLOBAL_DATA_PTR; |
|
|
|
|
#define GPIO_PORTA_EOI 0x4c |
|
|
|
|
#define GPIO_EXT_PORT(p) (0x50 + (p) * 4) |
|
|
|
|
|
|
|
|
|
struct gpio_dwapb_priv { |
|
|
|
|
struct reset_ctl_bulk resets; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct gpio_dwapb_platdata { |
|
|
|
|
const char *name; |
|
|
|
|
int bank; |
|
|
|
@ -78,20 +83,63 @@ static int dwapb_gpio_set_value(struct udevice *dev, unsigned pin, int val) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int dwapb_gpio_get_function(struct udevice *dev, unsigned offset) |
|
|
|
|
{ |
|
|
|
|
struct gpio_dwapb_platdata *plat = dev_get_platdata(dev); |
|
|
|
|
u32 gpio; |
|
|
|
|
|
|
|
|
|
gpio = readl(plat->base + GPIO_SWPORT_DDR(plat->bank)); |
|
|
|
|
|
|
|
|
|
if (gpio & BIT(offset)) |
|
|
|
|
return GPIOF_OUTPUT; |
|
|
|
|
else |
|
|
|
|
return GPIOF_INPUT; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const struct dm_gpio_ops gpio_dwapb_ops = { |
|
|
|
|
.direction_input = dwapb_gpio_direction_input, |
|
|
|
|
.direction_output = dwapb_gpio_direction_output, |
|
|
|
|
.get_value = dwapb_gpio_get_value, |
|
|
|
|
.set_value = dwapb_gpio_set_value, |
|
|
|
|
.get_function = dwapb_gpio_get_function, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static int gpio_dwapb_reset(struct udevice *dev) |
|
|
|
|
{ |
|
|
|
|
int ret; |
|
|
|
|
struct gpio_dwapb_priv *priv = dev_get_priv(dev); |
|
|
|
|
|
|
|
|
|
ret = reset_get_bulk(dev, &priv->resets); |
|
|
|
|
if (ret) { |
|
|
|
|
/* Return 0 if error due to !CONFIG_DM_RESET and reset
|
|
|
|
|
* DT property is not present. |
|
|
|
|
*/ |
|
|
|
|
if (ret == -ENOENT || ret == -ENOTSUPP) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
dev_warn(dev, "Can't get reset: %d\n", ret); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ret = reset_deassert_bulk(&priv->resets); |
|
|
|
|
if (ret) { |
|
|
|
|
reset_release_bulk(&priv->resets); |
|
|
|
|
dev_err(dev, "Failed to reset: %d\n", ret); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int gpio_dwapb_probe(struct udevice *dev) |
|
|
|
|
{ |
|
|
|
|
struct gpio_dev_priv *priv = dev_get_uclass_priv(dev); |
|
|
|
|
struct gpio_dwapb_platdata *plat = dev->platdata; |
|
|
|
|
|
|
|
|
|
if (!plat) |
|
|
|
|
return 0; |
|
|
|
|
if (!plat) { |
|
|
|
|
/* Reset on parent device only */ |
|
|
|
|
return gpio_dwapb_reset(dev); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
priv->gpio_count = plat->pins; |
|
|
|
|
priv->bank_name = plat->name; |
|
|
|
@ -111,7 +159,7 @@ static int gpio_dwapb_bind(struct udevice *dev) |
|
|
|
|
if (plat) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
base = fdtdec_get_addr(blob, dev_of_offset(dev), "reg"); |
|
|
|
|
base = dev_read_addr(dev); |
|
|
|
|
if (base == FDT_ADDR_T_NONE) { |
|
|
|
|
debug("Can't get the GPIO register base address\n"); |
|
|
|
|
return -ENXIO; |
|
|
|
@ -152,6 +200,17 @@ err: |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int gpio_dwapb_remove(struct udevice *dev) |
|
|
|
|
{ |
|
|
|
|
struct gpio_dwapb_platdata *plat = dev_get_platdata(dev); |
|
|
|
|
struct gpio_dwapb_priv *priv = dev_get_priv(dev); |
|
|
|
|
|
|
|
|
|
if (!plat && priv) |
|
|
|
|
return reset_release_bulk(&priv->resets); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const struct udevice_id gpio_dwapb_ids[] = { |
|
|
|
|
{ .compatible = "snps,dw-apb-gpio" }, |
|
|
|
|
{ } |
|
|
|
@ -164,4 +223,6 @@ U_BOOT_DRIVER(gpio_dwapb) = { |
|
|
|
|
.ops = &gpio_dwapb_ops, |
|
|
|
|
.bind = gpio_dwapb_bind, |
|
|
|
|
.probe = gpio_dwapb_probe, |
|
|
|
|
.remove = gpio_dwapb_remove, |
|
|
|
|
.priv_auto_alloc_size = sizeof(struct gpio_dwapb_priv), |
|
|
|
|
}; |
|
|
|
|