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 CONFIG_AXP221_POWER /* Only available on axp221/axp223 */ 63 case SUNXI_GPIO_AXP0_VBUS_ENABLE: 64 ret = pmic_bus_clrbits(AXP221_MISC_CTRL, 65 AXP221_MISC_CTRL_N_VBUSEN_FUNC); 66 if (ret) 67 return ret; 68 69 return axp_gpio_set_value(dev, pin, val); 70 #endif 71 default: 72 reg = axp_get_gpio_ctrl_reg(pin); 73 if (reg == 0) 74 return -EINVAL; 75 76 return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : 77 AXP_GPIO_CTRL_OUTPUT_LOW); 78 } 79 } 80 81 static int axp_gpio_get_value(struct udevice *dev, unsigned pin) 82 { 83 u8 reg, val, mask; 84 int ret; 85 86 switch (pin) { 87 #ifndef CONFIG_AXP152_POWER /* NA on axp152 */ 88 case SUNXI_GPIO_AXP0_VBUS_DETECT: 89 ret = pmic_bus_read(AXP_POWER_STATUS, &val); 90 mask = AXP_POWER_STATUS_VBUS_PRESENT; 91 break; 92 #endif 93 #ifdef CONFIG_AXP221_POWER /* Only available on axp221/axp223 */ 94 case SUNXI_GPIO_AXP0_VBUS_ENABLE: 95 ret = pmic_bus_read(AXP221_VBUS_IPSOUT, &val); 96 mask = AXP221_VBUS_IPSOUT_DRIVEBUS; 97 break; 98 #endif 99 default: 100 reg = axp_get_gpio_ctrl_reg(pin); 101 if (reg == 0) 102 return -EINVAL; 103 104 ret = pmic_bus_read(AXP_GPIO_STATE, &val); 105 mask = 1 << (pin + AXP_GPIO_STATE_OFFSET); 106 } 107 if (ret) 108 return ret; 109 110 return (val & mask) ? 1 : 0; 111 } 112 113 static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val) 114 { 115 u8 reg; 116 117 switch (pin) { 118 #ifdef CONFIG_AXP221_POWER /* Only available on axp221/axp223 */ 119 case SUNXI_GPIO_AXP0_VBUS_ENABLE: 120 if (val) 121 return pmic_bus_setbits(AXP221_VBUS_IPSOUT, 122 AXP221_VBUS_IPSOUT_DRIVEBUS); 123 else 124 return pmic_bus_clrbits(AXP221_VBUS_IPSOUT, 125 AXP221_VBUS_IPSOUT_DRIVEBUS); 126 #endif 127 default: 128 reg = axp_get_gpio_ctrl_reg(pin); 129 if (reg == 0) 130 return -EINVAL; 131 132 return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : 133 AXP_GPIO_CTRL_OUTPUT_LOW); 134 } 135 } 136 137 static const struct dm_gpio_ops gpio_axp_ops = { 138 .direction_input = axp_gpio_direction_input, 139 .direction_output = axp_gpio_direction_output, 140 .get_value = axp_gpio_get_value, 141 .set_value = axp_gpio_set_value, 142 }; 143 144 static int gpio_axp_probe(struct udevice *dev) 145 { 146 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 147 148 /* Tell the uclass how many GPIOs we have */ 149 uc_priv->bank_name = strdup(SUNXI_GPIO_AXP0_PREFIX); 150 uc_priv->gpio_count = SUNXI_GPIO_AXP0_GPIO_COUNT; 151 152 return 0; 153 } 154 155 U_BOOT_DRIVER(gpio_axp) = { 156 .name = "gpio_axp", 157 .id = UCLASS_GPIO, 158 .ops = &gpio_axp_ops, 159 .probe = gpio_axp_probe, 160 }; 161 162 int axp_gpio_init(void) 163 { 164 struct udevice *dev; 165 int ret; 166 167 ret = pmic_bus_init(); 168 if (ret) 169 return ret; 170 171 /* There is no devicetree support for the axp yet, so bind directly */ 172 ret = device_bind_driver(dm_root(), "gpio_axp", "AXP-gpio", &dev); 173 if (ret) 174 return ret; 175 176 return 0; 177 } 178