mfd: sc27xx: Add USB charger type detection support
[linux-2.6-microblaze.git] / drivers / mfd / sprd-sc27xx-spi.c
index c0529a1..ebdf2f1 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/of_device.h>
 #include <linux/regmap.h>
 #include <linux/spi/spi.h>
+#include <uapi/linux/usb/charger.h>
 
 #define SPRD_PMIC_INT_MASK_STATUS      0x0
 #define SPRD_PMIC_INT_RAW_STATUS       0x4
 
 #define SPRD_SC2731_IRQ_BASE           0x140
 #define SPRD_SC2731_IRQ_NUMS           16
+#define SPRD_SC2731_CHG_DET            0xedc
+
+/* PMIC charger detection definition */
+#define SPRD_PMIC_CHG_DET_DELAY_US     200000
+#define SPRD_PMIC_CHG_DET_TIMEOUT      2000000
+#define SPRD_PMIC_CHG_DET_DONE         BIT(11)
+#define SPRD_PMIC_SDP_TYPE             BIT(7)
+#define SPRD_PMIC_DCP_TYPE             BIT(6)
+#define SPRD_PMIC_CDP_TYPE             BIT(5)
+#define SPRD_PMIC_CHG_TYPE_MASK                GENMASK(7, 5)
 
 struct sprd_pmic {
        struct regmap *regmap;
@@ -24,12 +35,14 @@ struct sprd_pmic {
        struct regmap_irq *irqs;
        struct regmap_irq_chip irq_chip;
        struct regmap_irq_chip_data *irq_data;
+       const struct sprd_pmic_data *pdata;
        int irq;
 };
 
 struct sprd_pmic_data {
        u32 irq_base;
        u32 num_irqs;
+       u32 charger_det;
 };
 
 /*
@@ -40,8 +53,46 @@ struct sprd_pmic_data {
 static const struct sprd_pmic_data sc2731_data = {
        .irq_base = SPRD_SC2731_IRQ_BASE,
        .num_irqs = SPRD_SC2731_IRQ_NUMS,
+       .charger_det = SPRD_SC2731_CHG_DET,
 };
 
+enum usb_charger_type sprd_pmic_detect_charger_type(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct sprd_pmic *ddata = spi_get_drvdata(spi);
+       const struct sprd_pmic_data *pdata = ddata->pdata;
+       enum usb_charger_type type;
+       u32 val;
+       int ret;
+
+       ret = regmap_read_poll_timeout(ddata->regmap, pdata->charger_det, val,
+                                      (val & SPRD_PMIC_CHG_DET_DONE),
+                                      SPRD_PMIC_CHG_DET_DELAY_US,
+                                      SPRD_PMIC_CHG_DET_TIMEOUT);
+       if (ret) {
+               dev_err(&spi->dev, "failed to detect charger type\n");
+               return UNKNOWN_TYPE;
+       }
+
+       switch (val & SPRD_PMIC_CHG_TYPE_MASK) {
+       case SPRD_PMIC_CDP_TYPE:
+               type = CDP_TYPE;
+               break;
+       case SPRD_PMIC_DCP_TYPE:
+               type = DCP_TYPE;
+               break;
+       case SPRD_PMIC_SDP_TYPE:
+               type = SDP_TYPE;
+               break;
+       default:
+               type = UNKNOWN_TYPE;
+               break;
+       }
+
+       return type;
+}
+EXPORT_SYMBOL_GPL(sprd_pmic_detect_charger_type);
+
 static const struct mfd_cell sprd_pmic_devs[] = {
        {
                .name = "sc27xx-wdt",
@@ -181,6 +232,7 @@ static int sprd_pmic_probe(struct spi_device *spi)
        spi_set_drvdata(spi, ddata);
        ddata->dev = &spi->dev;
        ddata->irq = spi->irq;
+       ddata->pdata = pdata;
 
        ddata->irq_chip.name = dev_name(&spi->dev);
        ddata->irq_chip.status_base =