/* * TI LM3632 MFD Driver * * Copyright 2015 Texas Instruments * * Author: Milo Kim * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #define LM3632_DEV_LCD_BIAS(_id) \ { \ .name = "lm3632-regulator", \ .id = _id, \ .of_compatible = "ti,lm3632-regulator", \ } #define LM3632_DEV_BL \ { \ .name = "lm3632-backlight", \ .of_compatible = "ti,lm3632-backlight", \ } static struct mfd_cell lm3632_devs[] = { /* 5 Regulators */ LM3632_DEV_LCD_BIAS(1), LM3632_DEV_LCD_BIAS(2), LM3632_DEV_LCD_BIAS(3), LM3632_DEV_LCD_BIAS(4), LM3632_DEV_LCD_BIAS(5), /* Backlight */ LM3632_DEV_BL, }; int lm3632_read_byte(struct lm3632 *lm3632, u8 reg, u8 *read) { int ret; unsigned int val; ret = regmap_read(lm3632->regmap, reg, &val); if (ret < 0) return ret; *read = (u8)val; return 0; } EXPORT_SYMBOL_GPL(lm3632_read_byte); int lm3632_write_byte(struct lm3632 *lm3632, u8 reg, u8 data) { return regmap_write(lm3632->regmap, reg, data); } EXPORT_SYMBOL_GPL(lm3632_write_byte); int lm3632_update_bits(struct lm3632 *lm3632, u8 reg, u8 mask, u8 data) { return regmap_update_bits(lm3632->regmap, reg, mask, data); } EXPORT_SYMBOL_GPL(lm3632_update_bits); static int lm3632_init_device(struct lm3632 *lm3632) { int ret; /* * Sequence * * 1) Enable nRST pin * 2) Delay about 1ms (bias delay 200us + EPROM read time 700us) * 3) Set LCD_EN bit to 1 */ ret = devm_gpio_request_one(lm3632->dev, lm3632->pdata->en_gpio, GPIOF_OUT_INIT_HIGH, "lm3632_hwen"); if (ret) return ret; usleep_range(1000, 1500); return lm3632_update_bits(lm3632, LM3632_REG_DEVCTRL, LM3632_LCD_EN_MASK, 1 << LM3632_LCD_EN_SHIFT); } static void lm3632_deinit_device(struct lm3632 *lm3632) { gpio_set_value(lm3632->pdata->en_gpio, 0); } static int lm3632_parse_dt(struct device *dev, struct lm3632 *lm3632) { struct device_node *node = dev->of_node; struct lm3632_platform_data *pdata; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; pdata->en_gpio = of_get_named_gpio(node, "ti,en-gpio", 0); if (pdata->en_gpio == -EPROBE_DEFER) return -EPROBE_DEFER; lm3632->pdata = pdata; return 0; } static struct regmap_config lm3632_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = LM3632_MAX_REGISTERS, }; static int lm3632_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct lm3632 *lm3632; struct device *dev = &cl->dev; struct lm3632_platform_data *pdata = dev_get_platdata(dev); int ret; lm3632 = devm_kzalloc(dev, sizeof(*lm3632), GFP_KERNEL); if (!lm3632) return -ENOMEM; lm3632->pdata = pdata; if (!pdata) { if (IS_ENABLED(CONFIG_OF)) ret = lm3632_parse_dt(dev, lm3632); else ret = -ENODEV; if (ret) return ret; } lm3632->regmap = devm_regmap_init_i2c(cl, &lm3632_regmap_config); if (IS_ERR(lm3632->regmap)) return PTR_ERR(lm3632->regmap); lm3632->dev = &cl->dev; i2c_set_clientdata(cl, lm3632); ret = lm3632_init_device(lm3632); if (ret) return ret; return mfd_add_devices(dev, -1, lm3632_devs, ARRAY_SIZE(lm3632_devs), NULL, 0, NULL); } static int lm3632_remove(struct i2c_client *cl) { struct lm3632 *lm3632 = i2c_get_clientdata(cl); lm3632_deinit_device(lm3632); mfd_remove_devices(lm3632->dev); return 0; } static const struct i2c_device_id lm3632_ids[] = { { "lm3632", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, lm3632_ids); #ifdef CONFIG_OF static const struct of_device_id lm3632_of_match[] = { { .compatible = "ti,lm3632", }, { } }; MODULE_DEVICE_TABLE(of, lm3632_of_match); #endif static struct i2c_driver lm3632_driver = { .probe = lm3632_probe, .remove = lm3632_remove, .driver = { .name = "lm3632", .owner = THIS_MODULE, .of_match_table = of_match_ptr(lm3632_of_match), }, .id_table = lm3632_ids, }; module_i2c_driver(lm3632_driver); MODULE_DESCRIPTION("TI LM3632 MFD Core"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL");