1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * extcon driver for Basin Cove PMIC 4 * 5 * Copyright (c) 2019, Intel Corporation. 6 * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 7 */ 8 9 #include <linux/extcon-provider.h> 10 #include <linux/interrupt.h> 11 #include <linux/mfd/intel_soc_pmic.h> 12 #include <linux/mfd/intel_soc_pmic_mrfld.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/regmap.h> 16 17 #include "extcon-intel.h" 18 19 #define BCOVE_USBIDCTRL 0x19 20 #define BCOVE_USBIDCTRL_ID BIT(0) 21 #define BCOVE_USBIDCTRL_ACA BIT(1) 22 #define BCOVE_USBIDCTRL_ALL (BCOVE_USBIDCTRL_ID | BCOVE_USBIDCTRL_ACA) 23 24 #define BCOVE_USBIDSTS 0x1a 25 #define BCOVE_USBIDSTS_GND BIT(0) 26 #define BCOVE_USBIDSTS_RARBRC_MASK GENMASK(2, 1) 27 #define BCOVE_USBIDSTS_RARBRC_SHIFT 1 28 #define BCOVE_USBIDSTS_NO_ACA 0 29 #define BCOVE_USBIDSTS_R_ID_A 1 30 #define BCOVE_USBIDSTS_R_ID_B 2 31 #define BCOVE_USBIDSTS_R_ID_C 3 32 #define BCOVE_USBIDSTS_FLOAT BIT(3) 33 #define BCOVE_USBIDSTS_SHORT BIT(4) 34 35 #define BCOVE_CHGRIRQ_ALL (BCOVE_CHGRIRQ_VBUSDET | BCOVE_CHGRIRQ_DCDET | \ 36 BCOVE_CHGRIRQ_BATTDET | BCOVE_CHGRIRQ_USBIDDET) 37 38 #define BCOVE_CHGRCTRL0 0x4b 39 #define BCOVE_CHGRCTRL0_CHGRRESET BIT(0) 40 #define BCOVE_CHGRCTRL0_EMRGCHREN BIT(1) 41 #define BCOVE_CHGRCTRL0_EXTCHRDIS BIT(2) 42 #define BCOVE_CHGRCTRL0_SWCONTROL BIT(3) 43 #define BCOVE_CHGRCTRL0_TTLCK BIT(4) 44 #define BCOVE_CHGRCTRL0_BIT_5 BIT(5) 45 #define BCOVE_CHGRCTRL0_BIT_6 BIT(6) 46 #define BCOVE_CHGRCTRL0_CHR_WDT_NOKICK BIT(7) 47 48 struct mrfld_extcon_data { 49 struct device *dev; 50 struct regmap *regmap; 51 struct extcon_dev *edev; 52 unsigned int status; 53 unsigned int id; 54 }; 55 56 static const unsigned int mrfld_extcon_cable[] = { 57 EXTCON_USB, 58 EXTCON_USB_HOST, 59 EXTCON_CHG_USB_SDP, 60 EXTCON_CHG_USB_CDP, 61 EXTCON_CHG_USB_DCP, 62 EXTCON_CHG_USB_ACA, 63 EXTCON_NONE, 64 }; 65 66 static int mrfld_extcon_clear(struct mrfld_extcon_data *data, unsigned int reg, 67 unsigned int mask) 68 { 69 return regmap_update_bits(data->regmap, reg, mask, 0x00); 70 } 71 72 static int mrfld_extcon_set(struct mrfld_extcon_data *data, unsigned int reg, 73 unsigned int mask) 74 { 75 return regmap_update_bits(data->regmap, reg, mask, 0xff); 76 } 77 78 static int mrfld_extcon_sw_control(struct mrfld_extcon_data *data, bool enable) 79 { 80 unsigned int mask = BCOVE_CHGRCTRL0_SWCONTROL; 81 struct device *dev = data->dev; 82 int ret; 83 84 if (enable) 85 ret = mrfld_extcon_set(data, BCOVE_CHGRCTRL0, mask); 86 else 87 ret = mrfld_extcon_clear(data, BCOVE_CHGRCTRL0, mask); 88 if (ret) 89 dev_err(dev, "can't set SW control: %d\n", ret); 90 return ret; 91 } 92 93 static int mrfld_extcon_get_id(struct mrfld_extcon_data *data) 94 { 95 struct regmap *regmap = data->regmap; 96 unsigned int id; 97 bool ground; 98 int ret; 99 100 ret = regmap_read(regmap, BCOVE_USBIDSTS, &id); 101 if (ret) 102 return ret; 103 104 if (id & BCOVE_USBIDSTS_FLOAT) 105 return INTEL_USB_ID_FLOAT; 106 107 switch ((id & BCOVE_USBIDSTS_RARBRC_MASK) >> BCOVE_USBIDSTS_RARBRC_SHIFT) { 108 case BCOVE_USBIDSTS_R_ID_A: 109 return INTEL_USB_RID_A; 110 case BCOVE_USBIDSTS_R_ID_B: 111 return INTEL_USB_RID_B; 112 case BCOVE_USBIDSTS_R_ID_C: 113 return INTEL_USB_RID_C; 114 } 115 116 /* 117 * PMIC A0 reports USBIDSTS_GND = 1 for ID_GND, 118 * but PMIC B0 reports USBIDSTS_GND = 0 for ID_GND. 119 * Thus we must check this bit at last. 120 */ 121 ground = id & BCOVE_USBIDSTS_GND; 122 switch ('A' + BCOVE_MAJOR(data->id)) { 123 case 'A': 124 return ground ? INTEL_USB_ID_GND : INTEL_USB_ID_FLOAT; 125 case 'B': 126 return ground ? INTEL_USB_ID_FLOAT : INTEL_USB_ID_GND; 127 } 128 129 /* Unknown or unsupported type */ 130 return INTEL_USB_ID_FLOAT; 131 } 132 133 static int mrfld_extcon_role_detect(struct mrfld_extcon_data *data) 134 { 135 unsigned int id; 136 bool usb_host; 137 int ret; 138 139 ret = mrfld_extcon_get_id(data); 140 if (ret < 0) 141 return ret; 142 143 id = ret; 144 145 usb_host = (id == INTEL_USB_ID_GND) || (id == INTEL_USB_RID_A); 146 extcon_set_state_sync(data->edev, EXTCON_USB_HOST, usb_host); 147 148 return 0; 149 } 150 151 static int mrfld_extcon_cable_detect(struct mrfld_extcon_data *data) 152 { 153 struct regmap *regmap = data->regmap; 154 unsigned int status, change; 155 int ret; 156 157 /* 158 * It seems SCU firmware clears the content of BCOVE_CHGRIRQ1 159 * and makes it useless for OS. Instead we compare a previously 160 * stored status to the current one, provided by BCOVE_SCHGRIRQ1. 161 */ 162 ret = regmap_read(regmap, BCOVE_SCHGRIRQ1, &status); 163 if (ret) 164 return ret; 165 166 change = status ^ data->status; 167 if (!change) 168 return -ENODATA; 169 170 if (change & BCOVE_CHGRIRQ_USBIDDET) { 171 ret = mrfld_extcon_role_detect(data); 172 if (ret) 173 return ret; 174 } 175 176 data->status = status; 177 178 return 0; 179 } 180 181 static irqreturn_t mrfld_extcon_interrupt(int irq, void *dev_id) 182 { 183 struct mrfld_extcon_data *data = dev_id; 184 int ret; 185 186 ret = mrfld_extcon_cable_detect(data); 187 188 mrfld_extcon_clear(data, BCOVE_MIRQLVL1, BCOVE_LVL1_CHGR); 189 190 return ret ? IRQ_NONE: IRQ_HANDLED; 191 } 192 193 static int mrfld_extcon_probe(struct platform_device *pdev) 194 { 195 struct device *dev = &pdev->dev; 196 struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); 197 struct regmap *regmap = pmic->regmap; 198 struct mrfld_extcon_data *data; 199 unsigned int status; 200 unsigned int id; 201 int irq, ret; 202 203 irq = platform_get_irq(pdev, 0); 204 if (irq < 0) 205 return irq; 206 207 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 208 if (!data) 209 return -ENOMEM; 210 211 data->dev = dev; 212 data->regmap = regmap; 213 214 data->edev = devm_extcon_dev_allocate(dev, mrfld_extcon_cable); 215 if (IS_ERR(data->edev)) 216 return PTR_ERR(data->edev); 217 218 ret = devm_extcon_dev_register(dev, data->edev); 219 if (ret < 0) 220 return dev_err_probe(dev, ret, "can't register extcon device\n"); 221 222 ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_extcon_interrupt, 223 IRQF_ONESHOT | IRQF_SHARED, pdev->name, 224 data); 225 if (ret) 226 return dev_err_probe(dev, ret, "can't register IRQ handler\n"); 227 228 ret = regmap_read(regmap, BCOVE_ID, &id); 229 if (ret) 230 return dev_err_probe(dev, ret, "can't read PMIC ID\n"); 231 232 data->id = id; 233 234 ret = mrfld_extcon_sw_control(data, true); 235 if (ret) 236 return ret; 237 238 /* Get initial state */ 239 mrfld_extcon_role_detect(data); 240 241 /* 242 * Cached status value is used for cable detection, see comments 243 * in mrfld_extcon_cable_detect(), we need to sync cached value 244 * with a real state of the hardware. 245 */ 246 regmap_read(regmap, BCOVE_SCHGRIRQ1, &status); 247 data->status = status; 248 249 mrfld_extcon_clear(data, BCOVE_MIRQLVL1, BCOVE_LVL1_CHGR); 250 mrfld_extcon_clear(data, BCOVE_MCHGRIRQ1, BCOVE_CHGRIRQ_ALL); 251 252 mrfld_extcon_set(data, BCOVE_USBIDCTRL, BCOVE_USBIDCTRL_ALL); 253 254 platform_set_drvdata(pdev, data); 255 256 return 0; 257 } 258 259 static void mrfld_extcon_remove(struct platform_device *pdev) 260 { 261 struct mrfld_extcon_data *data = platform_get_drvdata(pdev); 262 263 mrfld_extcon_sw_control(data, false); 264 } 265 266 static const struct platform_device_id mrfld_extcon_id_table[] = { 267 { .name = "mrfld_bcove_pwrsrc" }, 268 {} 269 }; 270 MODULE_DEVICE_TABLE(platform, mrfld_extcon_id_table); 271 272 static struct platform_driver mrfld_extcon_driver = { 273 .driver = { 274 .name = "mrfld_bcove_pwrsrc", 275 }, 276 .probe = mrfld_extcon_probe, 277 .remove = mrfld_extcon_remove, 278 .id_table = mrfld_extcon_id_table, 279 }; 280 module_platform_driver(mrfld_extcon_driver); 281 282 MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); 283 MODULE_DESCRIPTION("extcon driver for Intel Merrifield Basin Cove PMIC"); 284 MODULE_LICENSE("GPL v2"); 285