root/drivers/extcon/extcon-intel-mrfld.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mrfld_extcon_clear
  2. mrfld_extcon_set
  3. mrfld_extcon_sw_control
  4. mrfld_extcon_get_id
  5. mrfld_extcon_role_detect
  6. mrfld_extcon_cable_detect
  7. mrfld_extcon_interrupt
  8. mrfld_extcon_probe
  9. mrfld_extcon_remove

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

/* [<][>][^][v][top][bottom][index][help] */