CHROMIUM: spring: add DT support for s5m8767.
authorTodd Broch <tbroch@chromium.org>
Sat, 26 Jan 2013 02:41:30 +0000 (18:41 -0800)
committerChromeBot <chrome-bot@google.com>
Fri, 8 Feb 2013 00:54:57 +0000 (16:54 -0800)
Add minimal device tree support for mfd/s5m-core and associated
s5m8767 PMIC.

BUG=chrome-os-partner:16430
TEST=kernel compiles

Signed-off-by: Todd Broch <tbroch@chromium.org>
Change-Id: Ieb74abb8ae061b727523c1b3010b354ce6023fe7
Reviewed-on: https://gerrit.chromium.org/gerrit/42202
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Commit-Queue: Todd Broch <tbroch@chromium.org>
Tested-by: Todd Broch <tbroch@chromium.org>
drivers/mfd/s5m-core.c
drivers/regulator/s5m8767.c
include/linux/mfd/s5m87xx/s5m-core.h
include/linux/mfd/s5m87xx/s5m-pmic.h

index 48949d9..601e598 100644 (file)
 #include <linux/mfd/s5m87xx/s5m-rtc.h>
 #include <linux/regmap.h>
 
+#ifdef CONFIG_OF
+static struct of_device_id __devinitdata s5m87xx_pmic_dt_match[] = {
+       {.compatible = "samsung,s5m8767-pmic"},
+       {},
+};
+#endif
+
 static struct mfd_cell s5m8751_devs[] = {
        {
                .name = "s5m8751-pmic",
@@ -89,6 +96,45 @@ static struct regmap_config s5m_regmap_config = {
        .val_bits = 8,
 };
 
+static inline int s5m87xx_i2c_get_driver_data(struct i2c_client *i2c,
+                                              const struct i2c_device_id *id)
+{
+#ifdef CONFIG_OF
+       if (i2c->dev.of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(s5m87xx_pmic_dt_match, i2c->dev.of_node);
+               return (int)match->data;
+       }
+#endif
+       return (int)id->driver_data;
+}
+
+#ifdef CONFIG_OF
+static struct s5m_platform_data *s5m87xx_i2c_parse_dt_pdata(struct device *dev)
+{
+       struct s5m_platform_data *pd;
+
+       pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+       if (!pd) {
+               dev_err(dev, "could not allocate memory for pdata\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (of_property_read_u32(dev->of_node, "s5m-core,device_type",
+                                &pd->device_type)) {
+               dev_warn(dev, "no OF device_type property");
+       } else {
+               dev_dbg(dev, "OF device_type property = %u", pd->device_type);
+       }
+       return pd;
+}
+#else
+static struct s5m_platform_data *s5m8767_i2c_parse_dt_pdata(struct device *dev)
+{
+       return 0;
+}
+#endif
+
 static int s5m87xx_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
@@ -105,14 +151,35 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,
        s5m87xx->dev = &i2c->dev;
        s5m87xx->i2c = i2c;
        s5m87xx->irq = i2c->irq;
-       s5m87xx->type = id->driver_data;
+       s5m87xx->type = s5m87xx_i2c_get_driver_data(i2c, id);
 
-       if (pdata) {
-               s5m87xx->device_type = pdata->device_type;
-               s5m87xx->ono = pdata->ono;
-               s5m87xx->irq_base = pdata->irq_base;
-               s5m87xx->wakeup = pdata->wakeup;
+       if (s5m87xx->dev->of_node) {
+               pdata = s5m87xx_i2c_parse_dt_pdata(s5m87xx->dev);
+               if (IS_ERR(pdata)) {
+                       ret = PTR_ERR(pdata);
+                       goto err;
+               }
        }
+       s5m87xx->pdata = pdata;
+
+       if (!pdata) {
+               ret = -ENODEV;
+               dev_warn(s5m87xx->dev, "No platform data found\n");
+               goto err;
+       }
+
+       s5m87xx->device_type = pdata->device_type;
+       /* TODO(tbroch): address whether we want this addtional interrupt node
+          and add it to DT parsing if yes.
+       */
+       s5m87xx->ono = pdata->ono;
+       /* TODO(tbroch): remove hack below and parse irq_base via DT */
+       s5m87xx->irq_base = pdata->irq_base = MAX77686_IRQ_BASE;
+#ifdef OF_CONFIG
+       s5m87xx->wakeup = i2c->flags & I2C_CLIENT_WAKE;
+#else
+       s5m87xx->wakeup = pdata->wakeup;
+#endif
 
        s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config);
        if (IS_ERR(s5m87xx->regmap)) {
@@ -184,6 +251,7 @@ static struct i2c_driver s5m87xx_i2c_driver = {
        .driver = {
                   .name = "s5m87xx",
                   .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(s5m87xx_pmic_dt_match),
        },
        .probe = s5m87xx_i2c_probe,
        .remove = s5m87xx_i2c_remove,
index 4ca2db0..ad7d8fc 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/mfd/s5m87xx/s5m-core.h>
 #include <linux/mfd/s5m87xx/s5m-pmic.h>
 
+#define S5M8767_OPMODE_MASK                    0x3
+
 struct s5m8767_info {
        struct device *dev;
        struct s5m87xx_dev *iodev;
@@ -525,10 +528,97 @@ static struct regulator_desc regulators[] = {
        regulator_desc_buck(9),
 };
 
+#ifdef CONFIG_OF
+static int s5m8767_pmic_dt_parse_pdata(struct s5m87xx_dev *iodev,
+                                       struct s5m_platform_data *pdata)
+{
+       struct device_node *pmic_np, *regulators_np, *reg_np;
+       struct s5m_regulator_data *rdata;
+       unsigned int i;
+
+       pmic_np = iodev->dev->of_node;
+       if (!pmic_np) {
+               dev_err(iodev->dev, "could not find pmic sub-node\n");
+               return -ENODEV;
+       }
+
+       regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators");
+       if (!regulators_np) {
+               dev_err(iodev->dev, "could not find regulators sub-node\n");
+               return -EINVAL;
+       }
+
+       /* count the number of regulators to be supported in pmic */
+       pdata->num_regulators = 0;
+       for_each_child_of_node(regulators_np, reg_np)
+               pdata->num_regulators++;
+
+       rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) *
+                               pdata->num_regulators, GFP_KERNEL);
+       if (!rdata) {
+               dev_err(iodev->dev,
+                       "could not allocate memory for regulator data\n");
+               return -ENOMEM;
+       }
+
+       pdata->regulators = rdata;
+       for_each_child_of_node(regulators_np, reg_np) {
+               for (i = 0; i < ARRAY_SIZE(regulators); i++)
+                       if (!of_node_cmp(reg_np->name, regulators[i].name))
+                               break;
+
+               if (i == ARRAY_SIZE(regulators)) {
+                       dev_warn(iodev->dev,
+                               "No configuration data for regulator %s\n",
+                               reg_np->name);
+                       continue;
+               }
+
+               rdata->id = i;
+               rdata->initdata = of_get_regulator_init_data(
+                                               iodev->dev, reg_np);
+               rdata->reg_node = reg_np;
+               if (of_property_read_u32(reg_np, "reg_op_mode",
+                               &rdata->reg_op_mode)) {
+                       dev_warn(iodev->dev, "no op_mode property property at %s\n",
+                               reg_np->full_name);
+                       /*
+                        * Set operating mode to NORMAL "ON" as default. The
+                        * 32KHz clocks are being turned on and kept on by
+                        * default, so the below mode setting does not impact
+                        * it.
+                        */
+                       rdata->reg_op_mode = S5M8767_OPMODE_MASK;
+               }
+               rdata++;
+       }
+
+       if (!of_property_read_u32(pmic_np, "s5m8767,buck_ramp_delay", &i))
+               pdata->buck_ramp_delay = i & 0xf;
+
+       if (of_get_property(pmic_np, "s5m8767,buck2_ramp_enable", NULL))
+               pdata->buck2_ramp_enable = 1;
+
+       if (of_get_property(pmic_np, "s5m8767,buck3_ramp_enable", NULL))
+               pdata->buck3_ramp_enable = 1;
+
+       if (of_get_property(pmic_np, "s5m8767,buck4_ramp_enable", NULL))
+               pdata->buck4_ramp_enable = 1;
+
+       return 0;
+}
+#else
+static int s5m8767_pmic_dt_parse_pdata(struct s5m87xx_dev *iodev,
+                                       struct s5m_platform_data *pdata)
+{
+       return 0;
+}
+#endif /* CONFIG_OF */
+
 static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
 {
        struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent);
-       struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev);
+       struct s5m_platform_data *pdata = iodev->pdata;
        struct regulator_dev **rdev;
        struct s5m8767_info *s5m8767;
        int i, ret, size;
@@ -538,6 +628,12 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
+       if (iodev->dev->of_node) {
+               ret = s5m8767_pmic_dt_parse_pdata(iodev, pdata);
+               if (ret)
+                       return ret;
+       }
+
        if (pdata->buck2_gpiodvs) {
                if (pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) {
                        dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n");
@@ -564,7 +660,10 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
        if (!s5m8767)
                return -ENOMEM;
 
-       size = sizeof(struct regulator_dev *) * (S5M8767_REG_MAX - 2);
+       if (!pdata->num_regulators)
+               pdata->num_regulators = S5M8767_REG_MAX - 2;
+
+       size = sizeof(struct regulator_dev *) * pdata->num_regulators;
        s5m8767->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
        if (!s5m8767->rdev)
                return -ENOMEM;
@@ -572,7 +671,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
        rdev = s5m8767->rdev;
        s5m8767->dev = &pdev->dev;
        s5m8767->iodev = iodev;
-       s5m8767->num_regulators = S5M8767_REG_MAX - 2;
+       s5m8767->num_regulators = pdata->num_regulators;
        platform_set_drvdata(pdev, s5m8767);
 
        s5m8767->buck_gpioindex = pdata->buck_default_idx;
@@ -724,7 +823,9 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
                                (desc->max - desc->min) / desc->step + 1;
 
                rdev[i] = regulator_register(&regulators[id], s5m8767->dev,
-                               pdata->regulators[i].initdata, s5m8767, NULL);
+                                            pdata->regulators[i].initdata,
+                                            s5m8767,
+                                            pdata->regulators[i].reg_node);
                if (IS_ERR(rdev[i])) {
                        ret = PTR_ERR(rdev[i]);
                        dev_err(s5m8767->dev, "regulator init failed for %d\n",
index a7480b5..eaf90ed 100644 (file)
@@ -304,6 +304,7 @@ enum s5m8763_irq {
  * @irq_masks_cur: currently active value
  * @irq_masks_cache: cached hardware value
  * @type: indicate which s5m87xx "variant" is used
+ * @pdata: platform data
  */
 struct s5m87xx_dev {
        struct device *dev;
@@ -321,6 +322,7 @@ struct s5m87xx_dev {
        u8 irq_masks_cache[NUM_IRQ_REGS];
        int type;
        bool wakeup;
+       struct s5m_platform_data *pdata;
 };
 
 int s5m_irq_init(struct s5m87xx_dev *s5m87xx);
index a72a5d2..863bbc1 100644 (file)
@@ -95,6 +95,8 @@ enum s5m8763_regulators {
 struct s5m_regulator_data {
        int                             id;
        struct regulator_init_data      *initdata;
+       struct device_node              *reg_node;
+       unsigned int                    reg_op_mode;
 };
 
 #endif /*  __LINUX_MFD_S5M_PMIC_H */