1074a1fddSStephen Warren /* 2074a1fddSStephen Warren * Copyright (c) 2010-2016, NVIDIA CORPORATION. 3074a1fddSStephen Warren * (based on tegra_gpio.c) 4074a1fddSStephen Warren * 5074a1fddSStephen Warren * SPDX-License-Identifier: GPL-2.0 6074a1fddSStephen Warren */ 7074a1fddSStephen Warren 8074a1fddSStephen Warren #include <common.h> 9074a1fddSStephen Warren #include <dm.h> 10074a1fddSStephen Warren #include <malloc.h> 11074a1fddSStephen Warren #include <errno.h> 12074a1fddSStephen Warren #include <fdtdec.h> 13074a1fddSStephen Warren #include <asm/io.h> 14074a1fddSStephen Warren #include <asm/bitops.h> 15074a1fddSStephen Warren #include <asm/gpio.h> 16074a1fddSStephen Warren #include <dm/device-internal.h> 17074a1fddSStephen Warren #include <dt-bindings/gpio/gpio.h> 18074a1fddSStephen Warren #include "tegra186_gpio_priv.h" 19074a1fddSStephen Warren 20074a1fddSStephen Warren DECLARE_GLOBAL_DATA_PTR; 21074a1fddSStephen Warren 22074a1fddSStephen Warren struct tegra186_gpio_port_data { 23074a1fddSStephen Warren const char *name; 24074a1fddSStephen Warren uint32_t offset; 25074a1fddSStephen Warren }; 26074a1fddSStephen Warren 27074a1fddSStephen Warren struct tegra186_gpio_ctlr_data { 28074a1fddSStephen Warren const struct tegra186_gpio_port_data *ports; 29074a1fddSStephen Warren uint32_t port_count; 30074a1fddSStephen Warren }; 31074a1fddSStephen Warren 32074a1fddSStephen Warren struct tegra186_gpio_platdata { 33074a1fddSStephen Warren const char *name; 34074a1fddSStephen Warren uint32_t *regs; 35074a1fddSStephen Warren }; 36074a1fddSStephen Warren 37074a1fddSStephen Warren static uint32_t *tegra186_gpio_reg(struct udevice *dev, uint32_t reg, 38074a1fddSStephen Warren uint32_t gpio) 39074a1fddSStephen Warren { 40074a1fddSStephen Warren struct tegra186_gpio_platdata *plat = dev->platdata; 41074a1fddSStephen Warren uint32_t index = (reg + (gpio * TEGRA186_GPIO_PER_GPIO_STRIDE)) / 4; 42074a1fddSStephen Warren 43074a1fddSStephen Warren return &(plat->regs[index]); 44074a1fddSStephen Warren } 45074a1fddSStephen Warren 46074a1fddSStephen Warren static int tegra186_gpio_set_out(struct udevice *dev, unsigned offset, 47074a1fddSStephen Warren bool output) 48074a1fddSStephen Warren { 49074a1fddSStephen Warren uint32_t *reg; 50074a1fddSStephen Warren uint32_t rval; 51074a1fddSStephen Warren 52074a1fddSStephen Warren reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_CONTROL, offset); 53074a1fddSStephen Warren rval = readl(reg); 54074a1fddSStephen Warren if (output) 55074a1fddSStephen Warren rval &= ~TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED; 56074a1fddSStephen Warren else 57074a1fddSStephen Warren rval |= TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED; 58074a1fddSStephen Warren writel(rval, reg); 59074a1fddSStephen Warren 60074a1fddSStephen Warren reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset); 61074a1fddSStephen Warren rval = readl(reg); 62074a1fddSStephen Warren if (output) 63074a1fddSStephen Warren rval |= TEGRA186_GPIO_ENABLE_CONFIG_OUT; 64074a1fddSStephen Warren else 65074a1fddSStephen Warren rval &= ~TEGRA186_GPIO_ENABLE_CONFIG_OUT; 66074a1fddSStephen Warren rval |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE; 67074a1fddSStephen Warren writel(rval, reg); 68074a1fddSStephen Warren 69074a1fddSStephen Warren return 0; 70074a1fddSStephen Warren } 71074a1fddSStephen Warren 72074a1fddSStephen Warren static int tegra186_gpio_set_val(struct udevice *dev, unsigned offset, bool val) 73074a1fddSStephen Warren { 74074a1fddSStephen Warren uint32_t *reg; 75074a1fddSStephen Warren uint32_t rval; 76074a1fddSStephen Warren 77074a1fddSStephen Warren reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE, offset); 78074a1fddSStephen Warren rval = readl(reg); 79074a1fddSStephen Warren if (val) 80074a1fddSStephen Warren rval |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; 81074a1fddSStephen Warren else 82074a1fddSStephen Warren rval &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; 83074a1fddSStephen Warren writel(rval, reg); 84074a1fddSStephen Warren 85074a1fddSStephen Warren return 0; 86074a1fddSStephen Warren } 87074a1fddSStephen Warren 88074a1fddSStephen Warren static int tegra186_gpio_direction_input(struct udevice *dev, unsigned offset) 89074a1fddSStephen Warren { 90074a1fddSStephen Warren return tegra186_gpio_set_out(dev, offset, false); 91074a1fddSStephen Warren } 92074a1fddSStephen Warren 93074a1fddSStephen Warren static int tegra186_gpio_direction_output(struct udevice *dev, unsigned offset, 94074a1fddSStephen Warren int value) 95074a1fddSStephen Warren { 96074a1fddSStephen Warren int ret; 97074a1fddSStephen Warren 98074a1fddSStephen Warren ret = tegra186_gpio_set_val(dev, offset, value != 0); 99074a1fddSStephen Warren if (ret) 100074a1fddSStephen Warren return ret; 101074a1fddSStephen Warren return tegra186_gpio_set_out(dev, offset, true); 102074a1fddSStephen Warren } 103074a1fddSStephen Warren 104074a1fddSStephen Warren static int tegra186_gpio_get_value(struct udevice *dev, unsigned offset) 105074a1fddSStephen Warren { 106074a1fddSStephen Warren uint32_t *reg; 107074a1fddSStephen Warren uint32_t rval; 108074a1fddSStephen Warren 109074a1fddSStephen Warren reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset); 110074a1fddSStephen Warren rval = readl(reg); 111074a1fddSStephen Warren 112074a1fddSStephen Warren if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT) 113074a1fddSStephen Warren reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE, 114074a1fddSStephen Warren offset); 115074a1fddSStephen Warren else 116074a1fddSStephen Warren reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_INPUT, offset); 117074a1fddSStephen Warren 118074a1fddSStephen Warren rval = readl(reg); 119074a1fddSStephen Warren return !!rval; 120074a1fddSStephen Warren } 121074a1fddSStephen Warren 122074a1fddSStephen Warren static int tegra186_gpio_set_value(struct udevice *dev, unsigned offset, 123074a1fddSStephen Warren int value) 124074a1fddSStephen Warren { 125074a1fddSStephen Warren return tegra186_gpio_set_val(dev, offset, value != 0); 126074a1fddSStephen Warren } 127074a1fddSStephen Warren 128074a1fddSStephen Warren static int tegra186_gpio_get_function(struct udevice *dev, unsigned offset) 129074a1fddSStephen Warren { 130074a1fddSStephen Warren uint32_t *reg; 131074a1fddSStephen Warren uint32_t rval; 132074a1fddSStephen Warren 133074a1fddSStephen Warren reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset); 134074a1fddSStephen Warren rval = readl(reg); 135074a1fddSStephen Warren if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT) 136074a1fddSStephen Warren return GPIOF_OUTPUT; 137074a1fddSStephen Warren else 138074a1fddSStephen Warren return GPIOF_INPUT; 139074a1fddSStephen Warren } 140074a1fddSStephen Warren 141074a1fddSStephen Warren static int tegra186_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, 142*3a57123eSSimon Glass struct ofnode_phandle_args *args) 143074a1fddSStephen Warren { 144074a1fddSStephen Warren int gpio, port, ret; 145074a1fddSStephen Warren 146074a1fddSStephen Warren gpio = args->args[0]; 147074a1fddSStephen Warren port = gpio / TEGRA186_GPIO_PER_GPIO_COUNT; 148074a1fddSStephen Warren ret = device_get_child(dev, port, &desc->dev); 149074a1fddSStephen Warren if (ret) 150074a1fddSStephen Warren return ret; 151074a1fddSStephen Warren desc->offset = gpio % TEGRA186_GPIO_PER_GPIO_COUNT; 152074a1fddSStephen Warren desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; 153074a1fddSStephen Warren 154074a1fddSStephen Warren return 0; 155074a1fddSStephen Warren } 156074a1fddSStephen Warren 157074a1fddSStephen Warren static const struct dm_gpio_ops tegra186_gpio_ops = { 158074a1fddSStephen Warren .direction_input = tegra186_gpio_direction_input, 159074a1fddSStephen Warren .direction_output = tegra186_gpio_direction_output, 160074a1fddSStephen Warren .get_value = tegra186_gpio_get_value, 161074a1fddSStephen Warren .set_value = tegra186_gpio_set_value, 162074a1fddSStephen Warren .get_function = tegra186_gpio_get_function, 163074a1fddSStephen Warren .xlate = tegra186_gpio_xlate, 164074a1fddSStephen Warren }; 165074a1fddSStephen Warren 166074a1fddSStephen Warren /** 167074a1fddSStephen Warren * We have a top-level GPIO device with no actual GPIOs. It has a child device 168074a1fddSStephen Warren * for each port within the controller. 169074a1fddSStephen Warren */ 170074a1fddSStephen Warren static int tegra186_gpio_bind(struct udevice *parent) 171074a1fddSStephen Warren { 172074a1fddSStephen Warren struct tegra186_gpio_platdata *parent_plat = parent->platdata; 173074a1fddSStephen Warren struct tegra186_gpio_ctlr_data *ctlr_data = 174074a1fddSStephen Warren (struct tegra186_gpio_ctlr_data *)dev_get_driver_data(parent); 175074a1fddSStephen Warren uint32_t *regs; 176074a1fddSStephen Warren int port, ret; 177074a1fddSStephen Warren 178074a1fddSStephen Warren /* If this is a child device, there is nothing to do here */ 179074a1fddSStephen Warren if (parent_plat) 180074a1fddSStephen Warren return 0; 181074a1fddSStephen Warren 182a821c4afSSimon Glass regs = (uint32_t *)devfdt_get_addr_name(parent, "gpio"); 183074a1fddSStephen Warren if (regs == (uint32_t *)FDT_ADDR_T_NONE) 184074a1fddSStephen Warren return -ENODEV; 185074a1fddSStephen Warren 186074a1fddSStephen Warren for (port = 0; port < ctlr_data->port_count; port++) { 187074a1fddSStephen Warren struct tegra186_gpio_platdata *plat; 188074a1fddSStephen Warren struct udevice *dev; 189074a1fddSStephen Warren 190074a1fddSStephen Warren plat = calloc(1, sizeof(*plat)); 191074a1fddSStephen Warren if (!plat) 192074a1fddSStephen Warren return -ENOMEM; 193074a1fddSStephen Warren plat->name = ctlr_data->ports[port].name; 194074a1fddSStephen Warren plat->regs = &(regs[ctlr_data->ports[port].offset / 4]); 195074a1fddSStephen Warren 196074a1fddSStephen Warren ret = device_bind(parent, parent->driver, plat->name, plat, 197074a1fddSStephen Warren -1, &dev); 198074a1fddSStephen Warren if (ret) 199074a1fddSStephen Warren return ret; 200e160f7d4SSimon Glass dev_set_of_offset(dev, dev_of_offset(parent)); 201074a1fddSStephen Warren } 202074a1fddSStephen Warren 203074a1fddSStephen Warren return 0; 204074a1fddSStephen Warren } 205074a1fddSStephen Warren 206074a1fddSStephen Warren static int tegra186_gpio_probe(struct udevice *dev) 207074a1fddSStephen Warren { 208074a1fddSStephen Warren struct tegra186_gpio_platdata *plat = dev->platdata; 209074a1fddSStephen Warren struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 210074a1fddSStephen Warren 211074a1fddSStephen Warren /* Only child devices have ports */ 212074a1fddSStephen Warren if (!plat) 213074a1fddSStephen Warren return 0; 214074a1fddSStephen Warren 215074a1fddSStephen Warren uc_priv->gpio_count = TEGRA186_GPIO_PER_GPIO_COUNT; 216074a1fddSStephen Warren uc_priv->bank_name = plat->name; 217074a1fddSStephen Warren 218074a1fddSStephen Warren return 0; 219074a1fddSStephen Warren } 220074a1fddSStephen Warren 221074a1fddSStephen Warren static const struct tegra186_gpio_port_data tegra186_gpio_main_ports[] = { 222074a1fddSStephen Warren {"A", 0x2000}, 223074a1fddSStephen Warren {"B", 0x3000}, 224074a1fddSStephen Warren {"C", 0x3200}, 225074a1fddSStephen Warren {"D", 0x3400}, 226074a1fddSStephen Warren {"E", 0x2200}, 227074a1fddSStephen Warren {"F", 0x2400}, 228074a1fddSStephen Warren {"G", 0x4200}, 229074a1fddSStephen Warren {"H", 0x1000}, 230074a1fddSStephen Warren {"I", 0x0800}, 231074a1fddSStephen Warren {"J", 0x5000}, 232074a1fddSStephen Warren {"K", 0x5200}, 233074a1fddSStephen Warren {"L", 0x1200}, 234074a1fddSStephen Warren {"M", 0x5600}, 235074a1fddSStephen Warren {"N", 0x0000}, 236074a1fddSStephen Warren {"O", 0x0200}, 237074a1fddSStephen Warren {"P", 0x4000}, 238074a1fddSStephen Warren {"Q", 0x0400}, 239074a1fddSStephen Warren {"R", 0x0a00}, 240074a1fddSStephen Warren {"T", 0x0600}, 241074a1fddSStephen Warren {"X", 0x1400}, 242074a1fddSStephen Warren {"Y", 0x1600}, 243074a1fddSStephen Warren {"BB", 0x2600}, 244074a1fddSStephen Warren {"CC", 0x5400}, 245074a1fddSStephen Warren }; 246074a1fddSStephen Warren 247074a1fddSStephen Warren static const struct tegra186_gpio_ctlr_data tegra186_gpio_main_data = { 248074a1fddSStephen Warren .ports = tegra186_gpio_main_ports, 249074a1fddSStephen Warren .port_count = ARRAY_SIZE(tegra186_gpio_main_ports), 250074a1fddSStephen Warren }; 251074a1fddSStephen Warren 252074a1fddSStephen Warren static const struct tegra186_gpio_port_data tegra186_gpio_aon_ports[] = { 253074a1fddSStephen Warren {"S", 0x0200}, 254074a1fddSStephen Warren {"U", 0x0400}, 255074a1fddSStephen Warren {"V", 0x0800}, 256074a1fddSStephen Warren {"W", 0x0a00}, 257074a1fddSStephen Warren {"Z", 0x0e00}, 258074a1fddSStephen Warren {"AA", 0x0c00}, 259074a1fddSStephen Warren {"EE", 0x0600}, 260074a1fddSStephen Warren {"FF", 0x0000}, 261074a1fddSStephen Warren }; 262074a1fddSStephen Warren 263074a1fddSStephen Warren static const struct tegra186_gpio_ctlr_data tegra186_gpio_aon_data = { 264074a1fddSStephen Warren .ports = tegra186_gpio_aon_ports, 265074a1fddSStephen Warren .port_count = ARRAY_SIZE(tegra186_gpio_aon_ports), 266074a1fddSStephen Warren }; 267074a1fddSStephen Warren 268074a1fddSStephen Warren static const struct udevice_id tegra186_gpio_ids[] = { 269074a1fddSStephen Warren { 270074a1fddSStephen Warren .compatible = "nvidia,tegra186-gpio", 271074a1fddSStephen Warren .data = (ulong)&tegra186_gpio_main_data, 272074a1fddSStephen Warren }, 273074a1fddSStephen Warren { 274074a1fddSStephen Warren .compatible = "nvidia,tegra186-gpio-aon", 275074a1fddSStephen Warren .data = (ulong)&tegra186_gpio_aon_data, 276074a1fddSStephen Warren }, 277074a1fddSStephen Warren { } 278074a1fddSStephen Warren }; 279074a1fddSStephen Warren 280074a1fddSStephen Warren U_BOOT_DRIVER(tegra186_gpio) = { 281074a1fddSStephen Warren .name = "tegra186_gpio", 282074a1fddSStephen Warren .id = UCLASS_GPIO, 283074a1fddSStephen Warren .of_match = tegra186_gpio_ids, 284074a1fddSStephen Warren .bind = tegra186_gpio_bind, 285074a1fddSStephen Warren .probe = tegra186_gpio_probe, 286074a1fddSStephen Warren .ops = &tegra186_gpio_ops, 287074a1fddSStephen Warren .flags = DM_FLAG_PRE_RELOC, 288074a1fddSStephen Warren }; 289