From: Todd Broch Date: Sat, 26 Jan 2013 02:41:30 +0000 (-0800) Subject: CHROMIUM: spring: add DT support for s5m8767. X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=4a6a40c8e93b677d97f977ce42318370ea418bec;p=cascardo%2Flinux.git CHROMIUM: spring: add DT support for s5m8767. 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 Change-Id: Ieb74abb8ae061b727523c1b3010b354ce6023fe7 Reviewed-on: https://gerrit.chromium.org/gerrit/42202 Reviewed-by: Vincent Palatin Commit-Queue: Todd Broch Tested-by: Todd Broch --- diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index 48949d998d10..601e59873d5a 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -26,6 +26,13 @@ #include #include +#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, diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 4ca2db059004..ad7d8fc6be12 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -20,9 +20,12 @@ #include #include #include +#include #include #include +#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(®ulators[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", diff --git a/include/linux/mfd/s5m87xx/s5m-core.h b/include/linux/mfd/s5m87xx/s5m-core.h index a7480b57f92d..eaf90edcb4bf 100644 --- a/include/linux/mfd/s5m87xx/s5m-core.h +++ b/include/linux/mfd/s5m87xx/s5m-core.h @@ -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); diff --git a/include/linux/mfd/s5m87xx/s5m-pmic.h b/include/linux/mfd/s5m87xx/s5m-pmic.h index a72a5d27e62e..863bbc155932 100644 --- a/include/linux/mfd/s5m87xx/s5m-pmic.h +++ b/include/linux/mfd/s5m87xx/s5m-pmic.h @@ -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 */