Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * extcon driver for Basin Cove PMIC
0004  *
0005  * Copyright (c) 2019, Intel Corporation.
0006  * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
0007  */
0008 
0009 #include <linux/extcon-provider.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/mfd/intel_soc_pmic.h>
0012 #include <linux/mfd/intel_soc_pmic_mrfld.h>
0013 #include <linux/mod_devicetable.h>
0014 #include <linux/module.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/regmap.h>
0017 
0018 #include "extcon-intel.h"
0019 
0020 #define BCOVE_USBIDCTRL         0x19
0021 #define BCOVE_USBIDCTRL_ID      BIT(0)
0022 #define BCOVE_USBIDCTRL_ACA     BIT(1)
0023 #define BCOVE_USBIDCTRL_ALL (BCOVE_USBIDCTRL_ID | BCOVE_USBIDCTRL_ACA)
0024 
0025 #define BCOVE_USBIDSTS          0x1a
0026 #define BCOVE_USBIDSTS_GND      BIT(0)
0027 #define BCOVE_USBIDSTS_RARBRC_MASK  GENMASK(2, 1)
0028 #define BCOVE_USBIDSTS_RARBRC_SHIFT 1
0029 #define BCOVE_USBIDSTS_NO_ACA       0
0030 #define BCOVE_USBIDSTS_R_ID_A       1
0031 #define BCOVE_USBIDSTS_R_ID_B       2
0032 #define BCOVE_USBIDSTS_R_ID_C       3
0033 #define BCOVE_USBIDSTS_FLOAT        BIT(3)
0034 #define BCOVE_USBIDSTS_SHORT        BIT(4)
0035 
0036 #define BCOVE_CHGRIRQ_ALL   (BCOVE_CHGRIRQ_VBUSDET | BCOVE_CHGRIRQ_DCDET | \
0037                  BCOVE_CHGRIRQ_BATTDET | BCOVE_CHGRIRQ_USBIDDET)
0038 
0039 #define BCOVE_CHGRCTRL0         0x4b
0040 #define BCOVE_CHGRCTRL0_CHGRRESET   BIT(0)
0041 #define BCOVE_CHGRCTRL0_EMRGCHREN   BIT(1)
0042 #define BCOVE_CHGRCTRL0_EXTCHRDIS   BIT(2)
0043 #define BCOVE_CHGRCTRL0_SWCONTROL   BIT(3)
0044 #define BCOVE_CHGRCTRL0_TTLCK       BIT(4)
0045 #define BCOVE_CHGRCTRL0_BIT_5       BIT(5)
0046 #define BCOVE_CHGRCTRL0_BIT_6       BIT(6)
0047 #define BCOVE_CHGRCTRL0_CHR_WDT_NOKICK  BIT(7)
0048 
0049 struct mrfld_extcon_data {
0050     struct device *dev;
0051     struct regmap *regmap;
0052     struct extcon_dev *edev;
0053     unsigned int status;
0054     unsigned int id;
0055 };
0056 
0057 static const unsigned int mrfld_extcon_cable[] = {
0058     EXTCON_USB,
0059     EXTCON_USB_HOST,
0060     EXTCON_CHG_USB_SDP,
0061     EXTCON_CHG_USB_CDP,
0062     EXTCON_CHG_USB_DCP,
0063     EXTCON_CHG_USB_ACA,
0064     EXTCON_NONE,
0065 };
0066 
0067 static int mrfld_extcon_clear(struct mrfld_extcon_data *data, unsigned int reg,
0068                   unsigned int mask)
0069 {
0070     return regmap_update_bits(data->regmap, reg, mask, 0x00);
0071 }
0072 
0073 static int mrfld_extcon_set(struct mrfld_extcon_data *data, unsigned int reg,
0074                 unsigned int mask)
0075 {
0076     return regmap_update_bits(data->regmap, reg, mask, 0xff);
0077 }
0078 
0079 static int mrfld_extcon_sw_control(struct mrfld_extcon_data *data, bool enable)
0080 {
0081     unsigned int mask = BCOVE_CHGRCTRL0_SWCONTROL;
0082     struct device *dev = data->dev;
0083     int ret;
0084 
0085     if (enable)
0086         ret = mrfld_extcon_set(data, BCOVE_CHGRCTRL0, mask);
0087     else
0088         ret = mrfld_extcon_clear(data, BCOVE_CHGRCTRL0, mask);
0089     if (ret)
0090         dev_err(dev, "can't set SW control: %d\n", ret);
0091     return ret;
0092 }
0093 
0094 static int mrfld_extcon_get_id(struct mrfld_extcon_data *data)
0095 {
0096     struct regmap *regmap = data->regmap;
0097     unsigned int id;
0098     bool ground;
0099     int ret;
0100 
0101     ret = regmap_read(regmap, BCOVE_USBIDSTS, &id);
0102     if (ret)
0103         return ret;
0104 
0105     if (id & BCOVE_USBIDSTS_FLOAT)
0106         return INTEL_USB_ID_FLOAT;
0107 
0108     switch ((id & BCOVE_USBIDSTS_RARBRC_MASK) >> BCOVE_USBIDSTS_RARBRC_SHIFT) {
0109     case BCOVE_USBIDSTS_R_ID_A:
0110         return INTEL_USB_RID_A;
0111     case BCOVE_USBIDSTS_R_ID_B:
0112         return INTEL_USB_RID_B;
0113     case BCOVE_USBIDSTS_R_ID_C:
0114         return INTEL_USB_RID_C;
0115     }
0116 
0117     /*
0118      * PMIC A0 reports USBIDSTS_GND = 1 for ID_GND,
0119      * but PMIC B0 reports USBIDSTS_GND = 0 for ID_GND.
0120      * Thus we must check this bit at last.
0121      */
0122     ground = id & BCOVE_USBIDSTS_GND;
0123     switch ('A' + BCOVE_MAJOR(data->id)) {
0124     case 'A':
0125         return ground ? INTEL_USB_ID_GND : INTEL_USB_ID_FLOAT;
0126     case 'B':
0127         return ground ? INTEL_USB_ID_FLOAT : INTEL_USB_ID_GND;
0128     }
0129 
0130     /* Unknown or unsupported type */
0131     return INTEL_USB_ID_FLOAT;
0132 }
0133 
0134 static int mrfld_extcon_role_detect(struct mrfld_extcon_data *data)
0135 {
0136     unsigned int id;
0137     bool usb_host;
0138     int ret;
0139 
0140     ret = mrfld_extcon_get_id(data);
0141     if (ret < 0)
0142         return ret;
0143 
0144     id = ret;
0145 
0146     usb_host = (id == INTEL_USB_ID_GND) || (id == INTEL_USB_RID_A);
0147     extcon_set_state_sync(data->edev, EXTCON_USB_HOST, usb_host);
0148 
0149     return 0;
0150 }
0151 
0152 static int mrfld_extcon_cable_detect(struct mrfld_extcon_data *data)
0153 {
0154     struct regmap *regmap = data->regmap;
0155     unsigned int status, change;
0156     int ret;
0157 
0158     /*
0159      * It seems SCU firmware clears the content of BCOVE_CHGRIRQ1
0160      * and makes it useless for OS. Instead we compare a previously
0161      * stored status to the current one, provided by BCOVE_SCHGRIRQ1.
0162      */
0163     ret = regmap_read(regmap, BCOVE_SCHGRIRQ1, &status);
0164     if (ret)
0165         return ret;
0166 
0167     change = status ^ data->status;
0168     if (!change)
0169         return -ENODATA;
0170 
0171     if (change & BCOVE_CHGRIRQ_USBIDDET) {
0172         ret = mrfld_extcon_role_detect(data);
0173         if (ret)
0174             return ret;
0175     }
0176 
0177     data->status = status;
0178 
0179     return 0;
0180 }
0181 
0182 static irqreturn_t mrfld_extcon_interrupt(int irq, void *dev_id)
0183 {
0184     struct mrfld_extcon_data *data = dev_id;
0185     int ret;
0186 
0187     ret = mrfld_extcon_cable_detect(data);
0188 
0189     mrfld_extcon_clear(data, BCOVE_MIRQLVL1, BCOVE_LVL1_CHGR);
0190 
0191     return ret ? IRQ_NONE: IRQ_HANDLED;
0192 }
0193 
0194 static int mrfld_extcon_probe(struct platform_device *pdev)
0195 {
0196     struct device *dev = &pdev->dev;
0197     struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
0198     struct regmap *regmap = pmic->regmap;
0199     struct mrfld_extcon_data *data;
0200     unsigned int status;
0201     unsigned int id;
0202     int irq, ret;
0203 
0204     irq = platform_get_irq(pdev, 0);
0205     if (irq < 0)
0206         return irq;
0207 
0208     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0209     if (!data)
0210         return -ENOMEM;
0211 
0212     data->dev = dev;
0213     data->regmap = regmap;
0214 
0215     data->edev = devm_extcon_dev_allocate(dev, mrfld_extcon_cable);
0216     if (IS_ERR(data->edev))
0217         return -ENOMEM;
0218 
0219     ret = devm_extcon_dev_register(dev, data->edev);
0220     if (ret < 0) {
0221         dev_err(dev, "can't register extcon device: %d\n", ret);
0222         return ret;
0223     }
0224 
0225     ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_extcon_interrupt,
0226                     IRQF_ONESHOT | IRQF_SHARED, pdev->name,
0227                     data);
0228     if (ret) {
0229         dev_err(dev, "can't register IRQ handler: %d\n", ret);
0230         return ret;
0231     }
0232 
0233     ret = regmap_read(regmap, BCOVE_ID, &id);
0234     if (ret) {
0235         dev_err(dev, "can't read PMIC ID: %d\n", ret);
0236         return ret;
0237     }
0238 
0239     data->id = id;
0240 
0241     ret = mrfld_extcon_sw_control(data, true);
0242     if (ret)
0243         return ret;
0244 
0245     /* Get initial state */
0246     mrfld_extcon_role_detect(data);
0247 
0248     /*
0249      * Cached status value is used for cable detection, see comments
0250      * in mrfld_extcon_cable_detect(), we need to sync cached value
0251      * with a real state of the hardware.
0252      */
0253     regmap_read(regmap, BCOVE_SCHGRIRQ1, &status);
0254     data->status = status;
0255 
0256     mrfld_extcon_clear(data, BCOVE_MIRQLVL1, BCOVE_LVL1_CHGR);
0257     mrfld_extcon_clear(data, BCOVE_MCHGRIRQ1, BCOVE_CHGRIRQ_ALL);
0258 
0259     mrfld_extcon_set(data, BCOVE_USBIDCTRL, BCOVE_USBIDCTRL_ALL);
0260 
0261     platform_set_drvdata(pdev, data);
0262 
0263     return 0;
0264 }
0265 
0266 static int mrfld_extcon_remove(struct platform_device *pdev)
0267 {
0268     struct mrfld_extcon_data *data = platform_get_drvdata(pdev);
0269 
0270     mrfld_extcon_sw_control(data, false);
0271 
0272     return 0;
0273 }
0274 
0275 static const struct platform_device_id mrfld_extcon_id_table[] = {
0276     { .name = "mrfld_bcove_pwrsrc" },
0277     {}
0278 };
0279 MODULE_DEVICE_TABLE(platform, mrfld_extcon_id_table);
0280 
0281 static struct platform_driver mrfld_extcon_driver = {
0282     .driver = {
0283         .name   = "mrfld_bcove_pwrsrc",
0284     },
0285     .probe      = mrfld_extcon_probe,
0286     .remove     = mrfld_extcon_remove,
0287     .id_table   = mrfld_extcon_id_table,
0288 };
0289 module_platform_driver(mrfld_extcon_driver);
0290 
0291 MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
0292 MODULE_DESCRIPTION("extcon driver for Intel Merrifield Basin Cove PMIC");
0293 MODULE_LICENSE("GPL v2");