X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=drivers%2Fmfd%2Fs5m-core.c;h=4b031e137cdeb6a0995abeef06406c2ec39b9a72;hb=d5142a9101a35f3344c5cb46e2df2645ba4fb50a;hp=48949d998d105f62172dae5697d3e4e3dd99819a;hpb=f187e9fd68577cdd5f914659b6f7f11124e40485;p=cascardo%2Flinux.git diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index 48949d998d10..4b031e137cde 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", @@ -54,46 +61,74 @@ static struct mfd_cell s5m8767_devs[] = { }, }; -int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest) -{ - return regmap_read(s5m87xx->regmap, reg, dest); -} -EXPORT_SYMBOL_GPL(s5m_reg_read); +static struct regmap_config s5m_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; -int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf) +static inline int s5m87xx_i2c_get_driver_data(struct i2c_client *i2c, + const struct i2c_device_id *id) { - return regmap_bulk_read(s5m87xx->regmap, reg, buf, count); +#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; } -EXPORT_SYMBOL_GPL(s5m_bulk_read); -int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value) +#ifdef CONFIG_OF +static struct s5m_platform_data *s5m87xx_i2c_parse_dt_pdata(struct device *dev) { - return regmap_write(s5m87xx->regmap, reg, value); -} -EXPORT_SYMBOL_GPL(s5m_reg_write); + struct s5m_platform_data *pd; -int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf) + 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"); + } + dev_dbg(dev, "OF device_type property = %u", pd->device_type); + + if (of_get_property(dev->of_node, "s5m-core,enable-low-jitter", NULL)) { + if (!(pd->device_type == S5M8767X)) + dev_warn(dev, "no low-jitter for this PMIC type\n"); + else + pd->low_jitter = true; + } + dev_dbg(dev, "OF low-jitter property: %u\n", pd->low_jitter); + + return pd; +} +#else +static struct s5m_platform_data *s5m87xx_i2c_parse_dt_pdata(struct device *dev) { - return regmap_raw_write(s5m87xx->regmap, reg, buf, count); + return 0; } -EXPORT_SYMBOL_GPL(s5m_bulk_write); +#endif -int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask) +static int s5m87xx_set_low_jitter(struct s5m87xx_dev *s5m87xx) { - return regmap_update_bits(s5m87xx->regmap, reg, mask, val); -} -EXPORT_SYMBOL_GPL(s5m_reg_update); + if (!s5m87xx->pdata->low_jitter) + return 0; -static struct regmap_config s5m_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; + return regmap_update_bits(s5m87xx->pmic, S5M8767_REG_CTRL1, + S5M8767_LOW_JITTER_MASK, + S5M8767_LOW_JITTER_MASK); +} -static int s5m87xx_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int __devinit s5m87xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct s5m_platform_data *pdata = i2c->dev.platform_data; struct s5m87xx_dev *s5m87xx; + struct i2c_client *rtc_i2c = NULL; int ret; s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev), @@ -103,27 +138,54 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, s5m87xx); 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 (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) { - s5m87xx->device_type = pdata->device_type; - s5m87xx->ono = pdata->ono; - s5m87xx->irq_base = pdata->irq_base; - s5m87xx->wakeup = pdata->wakeup; + if (!pdata) { + ret = -ENODEV; + dev_warn(s5m87xx->dev, "No platform data found\n"); + goto err; } - s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config); - if (IS_ERR(s5m87xx->regmap)) { - ret = PTR_ERR(s5m87xx->regmap); + 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->pmic = regmap_init_i2c(i2c, &s5m_regmap_config); + if (IS_ERR(s5m87xx->pmic)) { + ret = PTR_ERR(s5m87xx->pmic); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); goto err; } - s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); - i2c_set_clientdata(s5m87xx->rtc, s5m87xx); + rtc_i2c = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); + i2c_set_clientdata(rtc_i2c, s5m87xx); + s5m87xx->rtc = regmap_init_i2c(rtc_i2c, &s5m_regmap_config); + if (IS_ERR(s5m87xx->rtc)) { + ret = PTR_ERR(s5m87xx->rtc); + dev_err(&rtc_i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto err; + } if (pdata && pdata->cfg_pmic_irq) pdata->cfg_pmic_irq(); @@ -135,15 +197,15 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, switch (s5m87xx->device_type) { case S5M8751X: ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs, - ARRAY_SIZE(s5m8751_devs), NULL, 0); + ARRAY_SIZE(s5m8751_devs), NULL, 0); break; case S5M8763X: ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs, - ARRAY_SIZE(s5m8763_devs), NULL, 0); + ARRAY_SIZE(s5m8763_devs), NULL, 0); break; case S5M8767X: ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs, - ARRAY_SIZE(s5m8767_devs), NULL, 0); + ARRAY_SIZE(s5m8767_devs), NULL, 0); break; default: /* If this happens the probe function is problem */ @@ -153,13 +215,21 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto err; + if (s5m87xx_set_low_jitter(s5m87xx) < 0) { + dev_err(s5m87xx->dev, "failed to configure low-jitter\n"); + ret = -EIO; + goto err; + } + return ret; err: mfd_remove_devices(s5m87xx->dev); s5m_irq_exit(s5m87xx); - i2c_unregister_device(s5m87xx->rtc); - regmap_exit(s5m87xx->regmap); + if (rtc_i2c) + i2c_unregister_device(rtc_i2c); + regmap_exit(s5m87xx->pmic); + regmap_exit(s5m87xx->rtc); return ret; } @@ -167,10 +237,12 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c) { struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); + device_init_wakeup(s5m87xx->dev, 0); + pm_runtime_set_suspended(s5m87xx->dev); mfd_remove_devices(s5m87xx->dev); s5m_irq_exit(s5m87xx); - i2c_unregister_device(s5m87xx->rtc); - regmap_exit(s5m87xx->regmap); + regmap_exit(s5m87xx->pmic); + regmap_exit(s5m87xx->rtc); return 0; } @@ -180,10 +252,44 @@ static const struct i2c_device_id s5m87xx_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id); +#ifdef CONFIG_PM +static int s5m87xx_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + enable_irq_wake(s5m87xx->irq); + + return 0; +} + +static int s5m87xx_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + disable_irq_wake(s5m87xx->irq); + + s5m_irq_resume(s5m87xx); + return 0; +} + +const struct dev_pm_ops s5m87xx_pm = { + .suspend = s5m87xx_suspend, + .resume = s5m87xx_resume, +}; +#endif + static struct i2c_driver s5m87xx_i2c_driver = { .driver = { .name = "s5m87xx", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &s5m87xx_pm, +#endif + .of_match_table = of_match_ptr(s5m87xx_pmic_dt_match), }, .probe = s5m87xx_i2c_probe, .remove = s5m87xx_i2c_remove,