qcom_eud.c (5965B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. 4 */ 5 6#include <linux/bitops.h> 7#include <linux/err.h> 8#include <linux/interrupt.h> 9#include <linux/io.h> 10#include <linux/iopoll.h> 11#include <linux/kernel.h> 12#include <linux/module.h> 13#include <linux/of.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16#include <linux/sysfs.h> 17#include <linux/usb/role.h> 18 19#define EUD_REG_INT1_EN_MASK 0x0024 20#define EUD_REG_INT_STATUS_1 0x0044 21#define EUD_REG_CTL_OUT_1 0x0074 22#define EUD_REG_VBUS_INT_CLR 0x0080 23#define EUD_REG_CSR_EUD_EN 0x1014 24#define EUD_REG_SW_ATTACH_DET 0x1018 25#define EUD_REG_EUD_EN2 0x0000 26 27#define EUD_ENABLE BIT(0) 28#define EUD_INT_PET_EUD BIT(0) 29#define EUD_INT_VBUS BIT(2) 30#define EUD_INT_SAFE_MODE BIT(4) 31#define EUD_INT_ALL (EUD_INT_VBUS | EUD_INT_SAFE_MODE) 32 33struct eud_chip { 34 struct device *dev; 35 struct usb_role_switch *role_sw; 36 void __iomem *base; 37 void __iomem *mode_mgr; 38 unsigned int int_status; 39 int irq; 40 bool enabled; 41 bool usb_attached; 42}; 43 44static int enable_eud(struct eud_chip *priv) 45{ 46 writel(EUD_ENABLE, priv->base + EUD_REG_CSR_EUD_EN); 47 writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE, 48 priv->base + EUD_REG_INT1_EN_MASK); 49 writel(1, priv->mode_mgr + EUD_REG_EUD_EN2); 50 51 return usb_role_switch_set_role(priv->role_sw, USB_ROLE_DEVICE); 52} 53 54static void disable_eud(struct eud_chip *priv) 55{ 56 writel(0, priv->base + EUD_REG_CSR_EUD_EN); 57 writel(0, priv->mode_mgr + EUD_REG_EUD_EN2); 58} 59 60static ssize_t enable_show(struct device *dev, 61 struct device_attribute *attr, char *buf) 62{ 63 struct eud_chip *chip = dev_get_drvdata(dev); 64 65 return sysfs_emit(buf, "%d\n", chip->enabled); 66} 67 68static ssize_t enable_store(struct device *dev, 69 struct device_attribute *attr, 70 const char *buf, size_t count) 71{ 72 struct eud_chip *chip = dev_get_drvdata(dev); 73 bool enable; 74 int ret; 75 76 if (kstrtobool(buf, &enable)) 77 return -EINVAL; 78 79 if (enable) { 80 ret = enable_eud(chip); 81 if (!ret) 82 chip->enabled = enable; 83 else 84 disable_eud(chip); 85 } else { 86 disable_eud(chip); 87 } 88 89 return count; 90} 91 92static DEVICE_ATTR_RW(enable); 93 94static struct attribute *eud_attrs[] = { 95 &dev_attr_enable.attr, 96 NULL, 97}; 98ATTRIBUTE_GROUPS(eud); 99 100static void usb_attach_detach(struct eud_chip *chip) 101{ 102 u32 reg; 103 104 /* read ctl_out_1[4] to find USB attach or detach event */ 105 reg = readl(chip->base + EUD_REG_CTL_OUT_1); 106 chip->usb_attached = reg & EUD_INT_SAFE_MODE; 107} 108 109static void pet_eud(struct eud_chip *chip) 110{ 111 u32 reg; 112 int ret; 113 114 /* When the EUD_INT_PET_EUD in SW_ATTACH_DET is set, the cable has been 115 * disconnected and we need to detach the pet to check if EUD is in safe 116 * mode before attaching again. 117 */ 118 reg = readl(chip->base + EUD_REG_SW_ATTACH_DET); 119 if (reg & EUD_INT_PET_EUD) { 120 /* Detach & Attach pet for EUD */ 121 writel(0, chip->base + EUD_REG_SW_ATTACH_DET); 122 /* Delay to make sure detach pet is done before attach pet */ 123 ret = readl_poll_timeout(chip->base + EUD_REG_SW_ATTACH_DET, 124 reg, (reg == 0), 1, 100); 125 if (ret) { 126 dev_err(chip->dev, "Detach pet failed\n"); 127 return; 128 } 129 } 130 /* Attach pet for EUD */ 131 writel(EUD_INT_PET_EUD, chip->base + EUD_REG_SW_ATTACH_DET); 132} 133 134static irqreturn_t handle_eud_irq(int irq, void *data) 135{ 136 struct eud_chip *chip = data; 137 u32 reg; 138 139 reg = readl(chip->base + EUD_REG_INT_STATUS_1); 140 switch (reg & EUD_INT_ALL) { 141 case EUD_INT_VBUS: 142 usb_attach_detach(chip); 143 return IRQ_WAKE_THREAD; 144 case EUD_INT_SAFE_MODE: 145 pet_eud(chip); 146 return IRQ_HANDLED; 147 default: 148 return IRQ_NONE; 149 } 150} 151 152static irqreturn_t handle_eud_irq_thread(int irq, void *data) 153{ 154 struct eud_chip *chip = data; 155 int ret; 156 157 if (chip->usb_attached) 158 ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_DEVICE); 159 else 160 ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_HOST); 161 if (ret) 162 dev_err(chip->dev, "failed to set role switch\n"); 163 164 /* set and clear vbus_int_clr[0] to clear interrupt */ 165 writel(BIT(0), chip->base + EUD_REG_VBUS_INT_CLR); 166 writel(0, chip->base + EUD_REG_VBUS_INT_CLR); 167 168 return IRQ_HANDLED; 169} 170 171static void eud_role_switch_release(void *data) 172{ 173 struct eud_chip *chip = data; 174 175 usb_role_switch_put(chip->role_sw); 176} 177 178static int eud_probe(struct platform_device *pdev) 179{ 180 struct eud_chip *chip; 181 int ret; 182 183 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 184 if (!chip) 185 return -ENOMEM; 186 187 chip->dev = &pdev->dev; 188 189 chip->role_sw = usb_role_switch_get(&pdev->dev); 190 if (IS_ERR(chip->role_sw)) 191 return dev_err_probe(chip->dev, PTR_ERR(chip->role_sw), 192 "failed to get role switch\n"); 193 194 ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip); 195 if (ret) 196 return dev_err_probe(chip->dev, ret, 197 "failed to add role switch release action\n"); 198 199 chip->base = devm_platform_ioremap_resource(pdev, 0); 200 if (IS_ERR(chip->base)) 201 return PTR_ERR(chip->base); 202 203 chip->mode_mgr = devm_platform_ioremap_resource(pdev, 1); 204 if (IS_ERR(chip->mode_mgr)) 205 return PTR_ERR(chip->mode_mgr); 206 207 chip->irq = platform_get_irq(pdev, 0); 208 ret = devm_request_threaded_irq(&pdev->dev, chip->irq, handle_eud_irq, 209 handle_eud_irq_thread, IRQF_ONESHOT, NULL, chip); 210 if (ret) 211 return dev_err_probe(chip->dev, ret, "failed to allocate irq\n"); 212 213 enable_irq_wake(chip->irq); 214 215 platform_set_drvdata(pdev, chip); 216 217 return 0; 218} 219 220static int eud_remove(struct platform_device *pdev) 221{ 222 struct eud_chip *chip = platform_get_drvdata(pdev); 223 224 if (chip->enabled) 225 disable_eud(chip); 226 227 device_init_wakeup(&pdev->dev, false); 228 disable_irq_wake(chip->irq); 229 230 return 0; 231} 232 233static const struct of_device_id eud_dt_match[] = { 234 { .compatible = "qcom,sc7280-eud" }, 235 { } 236}; 237MODULE_DEVICE_TABLE(of, eud_dt_match); 238 239static struct platform_driver eud_driver = { 240 .probe = eud_probe, 241 .remove = eud_remove, 242 .driver = { 243 .name = "qcom_eud", 244 .dev_groups = eud_groups, 245 .of_match_table = eud_dt_match, 246 }, 247}; 248module_platform_driver(eud_driver); 249 250MODULE_DESCRIPTION("QTI EUD driver"); 251MODULE_LICENSE("GPL v2");