1 /* 2 * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> 3 * 4 * X-Powers AXP Power Management ICs gpio driver 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <common.h> 10 #include <asm/arch/gpio.h> 11 #include <asm/arch/pmic_bus.h> 12 #include <asm/gpio.h> 13 #include <axp_pmic.h> 14 #include <dm.h> 15 #include <dm/device-internal.h> 16 #include <dm/lists.h> 17 #include <dm/root.h> 18 #include <errno.h> 19 20 static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val); 21 22 static u8 axp_get_gpio_ctrl_reg(unsigned pin) 23 { 24 switch (pin) { 25 case 0: return AXP_GPIO0_CTRL; 26 case 1: return AXP_GPIO1_CTRL; 27 #ifdef AXP_GPIO2_CTRL 28 case 2: return AXP_GPIO2_CTRL; 29 #endif 30 #ifdef AXP_GPIO3_CTRL 31 case 3: return AXP_GPIO3_CTRL; 32 #endif 33 } 34 return 0; 35 } 36 37 static int axp_gpio_direction_input(struct udevice *dev, unsigned pin) 38 { 39 u8 reg; 40 41 switch (pin) { 42 #ifndef CONFIG_AXP152_POWER /* NA on axp152 */ 43 case SUNXI_GPIO_AXP0_VBUS_DETECT: 44 return 0; 45 #endif 46 default: 47 reg = axp_get_gpio_ctrl_reg(pin); 48 if (reg == 0) 49 return -EINVAL; 50 51 return pmic_bus_write(reg, AXP_GPIO_CTRL_INPUT); 52 } 53 } 54 55 static int axp_gpio_direction_output(struct udevice *dev, unsigned pin, 56 int val) 57 { 58 __maybe_unused int ret; 59 u8 reg; 60 61 switch (pin) { 62 #ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC 63 /* Only available on later PMICs */ 64 case SUNXI_GPIO_AXP0_VBUS_ENABLE: 65 ret = pmic_bus_clrbits(AXP_MISC_CTRL, 66 AXP_MISC_CTRL_N_VBUSEN_FUNC); 67 if (ret) 68 return ret; 69 70 return axp_gpio_set_value(dev, pin, val); 71 #endif 72 default: 73 reg = axp_get_gpio_ctrl_reg(pin); 74 if (reg == 0) 75 return -EINVAL; 76 77 return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : 78 AXP_GPIO_CTRL_OUTPUT_LOW); 79 } 80 } 81 82 static int axp_gpio_get_value(struct udevice *dev, unsigned pin) 83 { 84 u8 reg, val, mask; 85 int ret; 86 87 switch (pin) { 88 #ifndef CONFIG_AXP152_POWER /* NA on axp152 */ 89 case SUNXI_GPIO_AXP0_VBUS_DETECT: 90 ret = pmic_bus_read(AXP_POWER_STATUS, &val); 91 mask = AXP_POWER_STATUS_VBUS_PRESENT; 92 break; 93 #endif 94 #ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC 95 /* Only available on later PMICs */ 96 case SUNXI_GPIO_AXP0_VBUS_ENABLE: 97 ret = pmic_bus_read(AXP_VBUS_IPSOUT, &val); 98 mask = AXP_VBUS_IPSOUT_DRIVEBUS; 99 break; 100 #endif 101 default: 102 reg = axp_get_gpio_ctrl_reg(pin); 103 if (reg == 0) 104 return -EINVAL; 105 106 ret = pmic_bus_read(AXP_GPIO_STATE, &val); 107 mask = 1 << (pin + AXP_GPIO_STATE_OFFSET); 108 } 109 if (ret) 110 return ret; 111 112 return (val & mask) ? 1 : 0; 113 } 114 115 static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val) 116 { 117 u8 reg; 118 119 switch (pin) { 120 #ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC 121 /* Only available on later PMICs */ 122 case SUNXI_GPIO_AXP0_VBUS_ENABLE: 123 if (val) 124 return pmic_bus_setbits(AXP_VBUS_IPSOUT, 125 AXP_VBUS_IPSOUT_DRIVEBUS); 126 else 127 return pmic_bus_clrbits(AXP_VBUS_IPSOUT, 128 AXP_VBUS_IPSOUT_DRIVEBUS); 129 #endif 130 default: 131 reg = axp_get_gpio_ctrl_reg(pin); 132 if (reg == 0) 133 return -EINVAL; 134 135 return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : 136 AXP_GPIO_CTRL_OUTPUT_LOW); 137 } 138 } 139 140 static const struct dm_gpio_ops gpio_axp_ops = { 141 .direction_input = axp_gpio_direction_input, 142 .direction_output = axp_gpio_direction_output, 143 .get_value = axp_gpio_get_value, 144 .set_value = axp_gpio_set_value, 145 }; 146 147 static int gpio_axp_probe(struct udevice *dev) 148 { 149 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 150 151 /* Tell the uclass how many GPIOs we have */ 152 uc_priv->bank_name = strdup(SUNXI_GPIO_AXP0_PREFIX); 153 uc_priv->gpio_count = SUNXI_GPIO_AXP0_GPIO_COUNT; 154 155 return 0; 156 } 157 158 U_BOOT_DRIVER(gpio_axp) = { 159 .name = "gpio_axp", 160 .id = UCLASS_GPIO, 161 .ops = &gpio_axp_ops, 162 .probe = gpio_axp_probe, 163 }; 164 165 int axp_gpio_init(void) 166 { 167 struct udevice *dev; 168 int ret; 169 170 ret = pmic_bus_init(); 171 if (ret) 172 return ret; 173 174 /* There is no devicetree support for the axp yet, so bind directly */ 175 ret = device_bind_driver(dm_root(), "gpio_axp", "AXP-gpio", &dev); 176 if (ret) 177 return ret; 178 179 return 0; 180 } 181