xref: /openbmc/linux/drivers/mfd/lm3533-core.c (revision 0848c94f)
116c5c023SJohan Hovold /*
216c5c023SJohan Hovold  * lm3533-core.c -- LM3533 Core
316c5c023SJohan Hovold  *
416c5c023SJohan Hovold  * Copyright (C) 2011-2012 Texas Instruments
516c5c023SJohan Hovold  *
616c5c023SJohan Hovold  * Author: Johan Hovold <jhovold@gmail.com>
716c5c023SJohan Hovold  *
816c5c023SJohan Hovold  * This program is free software; you can redistribute it and/or modify it
916c5c023SJohan Hovold  * under  the terms of the GNU General  Public License as published by the
1016c5c023SJohan Hovold  * Free Software Foundation;  either version 2 of the License, or (at your
1116c5c023SJohan Hovold  * option) any later version.
1216c5c023SJohan Hovold  */
1316c5c023SJohan Hovold 
1416c5c023SJohan Hovold #include <linux/module.h>
1516c5c023SJohan Hovold #include <linux/init.h>
1616c5c023SJohan Hovold #include <linux/kernel.h>
1716c5c023SJohan Hovold #include <linux/err.h>
1816c5c023SJohan Hovold #include <linux/gpio.h>
1916c5c023SJohan Hovold #include <linux/i2c.h>
2016c5c023SJohan Hovold #include <linux/mfd/core.h>
2116c5c023SJohan Hovold #include <linux/regmap.h>
2216c5c023SJohan Hovold #include <linux/seq_file.h>
2316c5c023SJohan Hovold #include <linux/slab.h>
2416c5c023SJohan Hovold #include <linux/uaccess.h>
2516c5c023SJohan Hovold 
2616c5c023SJohan Hovold #include <linux/mfd/lm3533.h>
2716c5c023SJohan Hovold 
2816c5c023SJohan Hovold 
2916c5c023SJohan Hovold #define LM3533_BOOST_OVP_MASK		0x06
3016c5c023SJohan Hovold #define LM3533_BOOST_OVP_SHIFT		1
3116c5c023SJohan Hovold 
3216c5c023SJohan Hovold #define LM3533_BOOST_FREQ_MASK		0x01
3316c5c023SJohan Hovold #define LM3533_BOOST_FREQ_SHIFT		0
3416c5c023SJohan Hovold 
3516c5c023SJohan Hovold #define LM3533_BL_ID_MASK		1
3616c5c023SJohan Hovold #define LM3533_LED_ID_MASK		3
3716c5c023SJohan Hovold #define LM3533_BL_ID_MAX		1
3816c5c023SJohan Hovold #define LM3533_LED_ID_MAX		3
3916c5c023SJohan Hovold 
4016c5c023SJohan Hovold #define LM3533_HVLED_ID_MAX		2
4116c5c023SJohan Hovold #define LM3533_LVLED_ID_MAX		5
4216c5c023SJohan Hovold 
4316c5c023SJohan Hovold #define LM3533_REG_OUTPUT_CONF1		0x10
4416c5c023SJohan Hovold #define LM3533_REG_OUTPUT_CONF2		0x11
4516c5c023SJohan Hovold #define LM3533_REG_BOOST_PWM		0x2c
4616c5c023SJohan Hovold 
4716c5c023SJohan Hovold #define LM3533_REG_MAX			0xb2
4816c5c023SJohan Hovold 
4916c5c023SJohan Hovold 
5016c5c023SJohan Hovold static struct mfd_cell lm3533_als_devs[] = {
5116c5c023SJohan Hovold 	{
5216c5c023SJohan Hovold 		.name	= "lm3533-als",
5316c5c023SJohan Hovold 		.id	= -1,
5416c5c023SJohan Hovold 	},
5516c5c023SJohan Hovold };
5616c5c023SJohan Hovold 
5716c5c023SJohan Hovold static struct mfd_cell lm3533_bl_devs[] = {
5816c5c023SJohan Hovold 	{
5916c5c023SJohan Hovold 		.name	= "lm3533-backlight",
6016c5c023SJohan Hovold 		.id	= 0,
6116c5c023SJohan Hovold 	},
6216c5c023SJohan Hovold 	{
6316c5c023SJohan Hovold 		.name	= "lm3533-backlight",
6416c5c023SJohan Hovold 		.id	= 1,
6516c5c023SJohan Hovold 	},
6616c5c023SJohan Hovold };
6716c5c023SJohan Hovold 
6816c5c023SJohan Hovold static struct mfd_cell lm3533_led_devs[] = {
6916c5c023SJohan Hovold 	{
7016c5c023SJohan Hovold 		.name	= "lm3533-leds",
7116c5c023SJohan Hovold 		.id	= 0,
7216c5c023SJohan Hovold 	},
7316c5c023SJohan Hovold 	{
7416c5c023SJohan Hovold 		.name	= "lm3533-leds",
7516c5c023SJohan Hovold 		.id	= 1,
7616c5c023SJohan Hovold 	},
7716c5c023SJohan Hovold 	{
7816c5c023SJohan Hovold 		.name	= "lm3533-leds",
7916c5c023SJohan Hovold 		.id	= 2,
8016c5c023SJohan Hovold 	},
8116c5c023SJohan Hovold 	{
8216c5c023SJohan Hovold 		.name	= "lm3533-leds",
8316c5c023SJohan Hovold 		.id	= 3,
8416c5c023SJohan Hovold 	},
8516c5c023SJohan Hovold };
8616c5c023SJohan Hovold 
8716c5c023SJohan Hovold int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val)
8816c5c023SJohan Hovold {
8916c5c023SJohan Hovold 	int tmp;
9016c5c023SJohan Hovold 	int ret;
9116c5c023SJohan Hovold 
9216c5c023SJohan Hovold 	ret = regmap_read(lm3533->regmap, reg, &tmp);
9316c5c023SJohan Hovold 	if (ret < 0) {
9416c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to read register %02x: %d\n",
9516c5c023SJohan Hovold 								reg, ret);
9616c5c023SJohan Hovold 		return ret;
9716c5c023SJohan Hovold 	}
9816c5c023SJohan Hovold 
9916c5c023SJohan Hovold 	*val = tmp;
10016c5c023SJohan Hovold 
10116c5c023SJohan Hovold 	dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val);
10216c5c023SJohan Hovold 
10316c5c023SJohan Hovold 	return ret;
10416c5c023SJohan Hovold }
10516c5c023SJohan Hovold EXPORT_SYMBOL_GPL(lm3533_read);
10616c5c023SJohan Hovold 
10716c5c023SJohan Hovold int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val)
10816c5c023SJohan Hovold {
10916c5c023SJohan Hovold 	int ret;
11016c5c023SJohan Hovold 
11116c5c023SJohan Hovold 	dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val);
11216c5c023SJohan Hovold 
11316c5c023SJohan Hovold 	ret = regmap_write(lm3533->regmap, reg, val);
11416c5c023SJohan Hovold 	if (ret < 0) {
11516c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to write register %02x: %d\n",
11616c5c023SJohan Hovold 								reg, ret);
11716c5c023SJohan Hovold 	}
11816c5c023SJohan Hovold 
11916c5c023SJohan Hovold 	return ret;
12016c5c023SJohan Hovold }
12116c5c023SJohan Hovold EXPORT_SYMBOL_GPL(lm3533_write);
12216c5c023SJohan Hovold 
12316c5c023SJohan Hovold int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask)
12416c5c023SJohan Hovold {
12516c5c023SJohan Hovold 	int ret;
12616c5c023SJohan Hovold 
12716c5c023SJohan Hovold 	dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask);
12816c5c023SJohan Hovold 
129664dd066SAxel Lin 	ret = regmap_update_bits(lm3533->regmap, reg, mask, val);
13016c5c023SJohan Hovold 	if (ret < 0) {
13116c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to update register %02x: %d\n",
13216c5c023SJohan Hovold 								reg, ret);
13316c5c023SJohan Hovold 	}
13416c5c023SJohan Hovold 
13516c5c023SJohan Hovold 	return ret;
13616c5c023SJohan Hovold }
13716c5c023SJohan Hovold EXPORT_SYMBOL_GPL(lm3533_update);
13816c5c023SJohan Hovold 
139d9055dc5SJohan Hovold static int lm3533_set_boost_freq(struct lm3533 *lm3533,
140d9055dc5SJohan Hovold 						enum lm3533_boost_freq freq)
141d9055dc5SJohan Hovold {
142d9055dc5SJohan Hovold 	int ret;
143d9055dc5SJohan Hovold 
144d9055dc5SJohan Hovold 	ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM,
145d9055dc5SJohan Hovold 					freq << LM3533_BOOST_FREQ_SHIFT,
146d9055dc5SJohan Hovold 					LM3533_BOOST_FREQ_MASK);
147d9055dc5SJohan Hovold 	if (ret)
148d9055dc5SJohan Hovold 		dev_err(lm3533->dev, "failed to set boost frequency\n");
149d9055dc5SJohan Hovold 
150d9055dc5SJohan Hovold 	return ret;
151d9055dc5SJohan Hovold }
152d9055dc5SJohan Hovold 
153d9055dc5SJohan Hovold 
154d9055dc5SJohan Hovold static int lm3533_set_boost_ovp(struct lm3533 *lm3533,
155d9055dc5SJohan Hovold 						enum lm3533_boost_ovp ovp)
156d9055dc5SJohan Hovold {
157d9055dc5SJohan Hovold 	int ret;
158d9055dc5SJohan Hovold 
159d9055dc5SJohan Hovold 	ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM,
160d9055dc5SJohan Hovold 					ovp << LM3533_BOOST_OVP_SHIFT,
161d9055dc5SJohan Hovold 					LM3533_BOOST_OVP_MASK);
162d9055dc5SJohan Hovold 	if (ret)
163d9055dc5SJohan Hovold 		dev_err(lm3533->dev, "failed to set boost ovp\n");
164d9055dc5SJohan Hovold 
165d9055dc5SJohan Hovold 	return ret;
166d9055dc5SJohan Hovold }
167d9055dc5SJohan Hovold 
16816c5c023SJohan Hovold /*
16916c5c023SJohan Hovold  * HVLED output config -- output hvled controlled by backlight bl
17016c5c023SJohan Hovold  */
17116c5c023SJohan Hovold static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl)
17216c5c023SJohan Hovold {
17316c5c023SJohan Hovold 	u8 val;
17416c5c023SJohan Hovold 	u8 mask;
17516c5c023SJohan Hovold 	int shift;
17616c5c023SJohan Hovold 	int ret;
17716c5c023SJohan Hovold 
17816c5c023SJohan Hovold 	if (hvled == 0 || hvled > LM3533_HVLED_ID_MAX)
17916c5c023SJohan Hovold 		return -EINVAL;
18016c5c023SJohan Hovold 
18116c5c023SJohan Hovold 	if (bl > LM3533_BL_ID_MAX)
18216c5c023SJohan Hovold 		return -EINVAL;
18316c5c023SJohan Hovold 
18416c5c023SJohan Hovold 	shift = hvled - 1;
18516c5c023SJohan Hovold 	mask = LM3533_BL_ID_MASK << shift;
18616c5c023SJohan Hovold 	val = bl << shift;
18716c5c023SJohan Hovold 
18816c5c023SJohan Hovold 	ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask);
18916c5c023SJohan Hovold 	if (ret)
19016c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to set hvled config\n");
19116c5c023SJohan Hovold 
19216c5c023SJohan Hovold 	return ret;
19316c5c023SJohan Hovold }
19416c5c023SJohan Hovold 
19516c5c023SJohan Hovold /*
19616c5c023SJohan Hovold  * LVLED output config -- output lvled controlled by LED led
19716c5c023SJohan Hovold  */
19816c5c023SJohan Hovold static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led)
19916c5c023SJohan Hovold {
20016c5c023SJohan Hovold 	u8 reg;
20116c5c023SJohan Hovold 	u8 val;
20216c5c023SJohan Hovold 	u8 mask;
20316c5c023SJohan Hovold 	int shift;
20416c5c023SJohan Hovold 	int ret;
20516c5c023SJohan Hovold 
20616c5c023SJohan Hovold 	if (lvled == 0 || lvled > LM3533_LVLED_ID_MAX)
20716c5c023SJohan Hovold 		return -EINVAL;
20816c5c023SJohan Hovold 
20916c5c023SJohan Hovold 	if (led > LM3533_LED_ID_MAX)
21016c5c023SJohan Hovold 		return -EINVAL;
21116c5c023SJohan Hovold 
21216c5c023SJohan Hovold 	if (lvled < 4) {
21316c5c023SJohan Hovold 		reg = LM3533_REG_OUTPUT_CONF1;
21416c5c023SJohan Hovold 		shift = 2 * lvled;
21516c5c023SJohan Hovold 	} else {
21616c5c023SJohan Hovold 		reg = LM3533_REG_OUTPUT_CONF2;
21716c5c023SJohan Hovold 		shift = 2 * (lvled - 4);
21816c5c023SJohan Hovold 	}
21916c5c023SJohan Hovold 
22016c5c023SJohan Hovold 	mask = LM3533_LED_ID_MASK << shift;
22116c5c023SJohan Hovold 	val = led << shift;
22216c5c023SJohan Hovold 
22316c5c023SJohan Hovold 	ret = lm3533_update(lm3533, reg, val, mask);
22416c5c023SJohan Hovold 	if (ret)
22516c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to set lvled config\n");
22616c5c023SJohan Hovold 
22716c5c023SJohan Hovold 	return ret;
22816c5c023SJohan Hovold }
22916c5c023SJohan Hovold 
23016c5c023SJohan Hovold static void lm3533_enable(struct lm3533 *lm3533)
23116c5c023SJohan Hovold {
23216c5c023SJohan Hovold 	if (gpio_is_valid(lm3533->gpio_hwen))
23316c5c023SJohan Hovold 		gpio_set_value(lm3533->gpio_hwen, 1);
23416c5c023SJohan Hovold }
23516c5c023SJohan Hovold 
23616c5c023SJohan Hovold static void lm3533_disable(struct lm3533 *lm3533)
23716c5c023SJohan Hovold {
23816c5c023SJohan Hovold 	if (gpio_is_valid(lm3533->gpio_hwen))
23916c5c023SJohan Hovold 		gpio_set_value(lm3533->gpio_hwen, 0);
24016c5c023SJohan Hovold }
24116c5c023SJohan Hovold 
24216c5c023SJohan Hovold enum lm3533_attribute_type {
24316c5c023SJohan Hovold 	LM3533_ATTR_TYPE_BACKLIGHT,
24416c5c023SJohan Hovold 	LM3533_ATTR_TYPE_LED,
24516c5c023SJohan Hovold };
24616c5c023SJohan Hovold 
24716c5c023SJohan Hovold struct lm3533_device_attribute {
24816c5c023SJohan Hovold 	struct device_attribute dev_attr;
24916c5c023SJohan Hovold 	enum lm3533_attribute_type type;
25016c5c023SJohan Hovold 	union {
25116c5c023SJohan Hovold 		struct {
25216c5c023SJohan Hovold 			u8 id;
25316c5c023SJohan Hovold 		} output;
25416c5c023SJohan Hovold 	} u;
25516c5c023SJohan Hovold };
25616c5c023SJohan Hovold 
25716c5c023SJohan Hovold #define to_lm3533_dev_attr(_attr) \
25816c5c023SJohan Hovold 	container_of(_attr, struct lm3533_device_attribute, dev_attr)
25916c5c023SJohan Hovold 
26016c5c023SJohan Hovold static ssize_t show_output(struct device *dev,
26116c5c023SJohan Hovold 				struct device_attribute *attr, char *buf)
26216c5c023SJohan Hovold {
26316c5c023SJohan Hovold 	struct lm3533 *lm3533 = dev_get_drvdata(dev);
26416c5c023SJohan Hovold 	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
26516c5c023SJohan Hovold 	int id = lattr->u.output.id;
26616c5c023SJohan Hovold 	u8 reg;
26716c5c023SJohan Hovold 	u8 val;
26816c5c023SJohan Hovold 	u8 mask;
26916c5c023SJohan Hovold 	int shift;
27016c5c023SJohan Hovold 	int ret;
27116c5c023SJohan Hovold 
27216c5c023SJohan Hovold 	if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) {
27316c5c023SJohan Hovold 		reg = LM3533_REG_OUTPUT_CONF1;
27416c5c023SJohan Hovold 		shift = id - 1;
27516c5c023SJohan Hovold 		mask = LM3533_BL_ID_MASK << shift;
27616c5c023SJohan Hovold 	} else {
27716c5c023SJohan Hovold 		if (id < 4) {
27816c5c023SJohan Hovold 			reg = LM3533_REG_OUTPUT_CONF1;
27916c5c023SJohan Hovold 			shift = 2 * id;
28016c5c023SJohan Hovold 		} else {
28116c5c023SJohan Hovold 			reg = LM3533_REG_OUTPUT_CONF2;
28216c5c023SJohan Hovold 			shift = 2 * (id - 4);
28316c5c023SJohan Hovold 		}
28416c5c023SJohan Hovold 		mask = LM3533_LED_ID_MASK << shift;
28516c5c023SJohan Hovold 	}
28616c5c023SJohan Hovold 
28716c5c023SJohan Hovold 	ret = lm3533_read(lm3533, reg, &val);
28816c5c023SJohan Hovold 	if (ret)
28916c5c023SJohan Hovold 		return ret;
29016c5c023SJohan Hovold 
29116c5c023SJohan Hovold 	val = (val & mask) >> shift;
29216c5c023SJohan Hovold 
29316c5c023SJohan Hovold 	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
29416c5c023SJohan Hovold }
29516c5c023SJohan Hovold 
29616c5c023SJohan Hovold static ssize_t store_output(struct device *dev,
29716c5c023SJohan Hovold 					struct device_attribute *attr,
29816c5c023SJohan Hovold 					const char *buf, size_t len)
29916c5c023SJohan Hovold {
30016c5c023SJohan Hovold 	struct lm3533 *lm3533 = dev_get_drvdata(dev);
30116c5c023SJohan Hovold 	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
30216c5c023SJohan Hovold 	int id = lattr->u.output.id;
30316c5c023SJohan Hovold 	u8 val;
30416c5c023SJohan Hovold 	int ret;
30516c5c023SJohan Hovold 
30616c5c023SJohan Hovold 	if (kstrtou8(buf, 0, &val))
30716c5c023SJohan Hovold 		return -EINVAL;
30816c5c023SJohan Hovold 
30916c5c023SJohan Hovold 	if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT)
31016c5c023SJohan Hovold 		ret = lm3533_set_hvled_config(lm3533, id, val);
31116c5c023SJohan Hovold 	else
31216c5c023SJohan Hovold 		ret = lm3533_set_lvled_config(lm3533, id, val);
31316c5c023SJohan Hovold 
31416c5c023SJohan Hovold 	if (ret)
31516c5c023SJohan Hovold 		return ret;
31616c5c023SJohan Hovold 
31716c5c023SJohan Hovold 	return len;
31816c5c023SJohan Hovold }
31916c5c023SJohan Hovold 
32016c5c023SJohan Hovold #define LM3533_OUTPUT_ATTR(_name, _mode, _show, _store, _type, _id) \
32116c5c023SJohan Hovold 	struct lm3533_device_attribute lm3533_dev_attr_##_name = \
32216c5c023SJohan Hovold 		{ .dev_attr	= __ATTR(_name, _mode, _show, _store), \
32316c5c023SJohan Hovold 		  .type		= _type, \
32416c5c023SJohan Hovold 		  .u.output	= { .id = _id }, }
32516c5c023SJohan Hovold 
32616c5c023SJohan Hovold #define LM3533_OUTPUT_ATTR_RW(_name, _type, _id) \
32716c5c023SJohan Hovold 	LM3533_OUTPUT_ATTR(output_##_name, S_IRUGO | S_IWUSR, \
32816c5c023SJohan Hovold 					show_output, store_output, _type, _id)
32916c5c023SJohan Hovold 
33016c5c023SJohan Hovold #define LM3533_OUTPUT_HVLED_ATTR_RW(_nr) \
33116c5c023SJohan Hovold 	LM3533_OUTPUT_ATTR_RW(hvled##_nr, LM3533_ATTR_TYPE_BACKLIGHT, _nr)
33216c5c023SJohan Hovold #define LM3533_OUTPUT_LVLED_ATTR_RW(_nr) \
33316c5c023SJohan Hovold 	LM3533_OUTPUT_ATTR_RW(lvled##_nr, LM3533_ATTR_TYPE_LED, _nr)
33416c5c023SJohan Hovold /*
33516c5c023SJohan Hovold  * Output config:
33616c5c023SJohan Hovold  *
33716c5c023SJohan Hovold  * output_hvled<nr>	0-1
33816c5c023SJohan Hovold  * output_lvled<nr>	0-3
33916c5c023SJohan Hovold  */
34016c5c023SJohan Hovold static LM3533_OUTPUT_HVLED_ATTR_RW(1);
34116c5c023SJohan Hovold static LM3533_OUTPUT_HVLED_ATTR_RW(2);
34216c5c023SJohan Hovold static LM3533_OUTPUT_LVLED_ATTR_RW(1);
34316c5c023SJohan Hovold static LM3533_OUTPUT_LVLED_ATTR_RW(2);
34416c5c023SJohan Hovold static LM3533_OUTPUT_LVLED_ATTR_RW(3);
34516c5c023SJohan Hovold static LM3533_OUTPUT_LVLED_ATTR_RW(4);
34616c5c023SJohan Hovold static LM3533_OUTPUT_LVLED_ATTR_RW(5);
34716c5c023SJohan Hovold 
34816c5c023SJohan Hovold static struct attribute *lm3533_attributes[] = {
34916c5c023SJohan Hovold 	&lm3533_dev_attr_output_hvled1.dev_attr.attr,
35016c5c023SJohan Hovold 	&lm3533_dev_attr_output_hvled2.dev_attr.attr,
35116c5c023SJohan Hovold 	&lm3533_dev_attr_output_lvled1.dev_attr.attr,
35216c5c023SJohan Hovold 	&lm3533_dev_attr_output_lvled2.dev_attr.attr,
35316c5c023SJohan Hovold 	&lm3533_dev_attr_output_lvled3.dev_attr.attr,
35416c5c023SJohan Hovold 	&lm3533_dev_attr_output_lvled4.dev_attr.attr,
35516c5c023SJohan Hovold 	&lm3533_dev_attr_output_lvled5.dev_attr.attr,
35616c5c023SJohan Hovold 	NULL,
35716c5c023SJohan Hovold };
35816c5c023SJohan Hovold 
35916c5c023SJohan Hovold #define to_dev_attr(_attr) \
36016c5c023SJohan Hovold 	container_of(_attr, struct device_attribute, attr)
36116c5c023SJohan Hovold 
362168755ebSDan Carpenter static umode_t lm3533_attr_is_visible(struct kobject *kobj,
36316c5c023SJohan Hovold 					     struct attribute *attr, int n)
36416c5c023SJohan Hovold {
36516c5c023SJohan Hovold 	struct device *dev = container_of(kobj, struct device, kobj);
36616c5c023SJohan Hovold 	struct lm3533 *lm3533 = dev_get_drvdata(dev);
36716c5c023SJohan Hovold 	struct device_attribute *dattr = to_dev_attr(attr);
36816c5c023SJohan Hovold 	struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr);
36916c5c023SJohan Hovold 	enum lm3533_attribute_type type = lattr->type;
37060908855SJohan Hovold 	umode_t mode = attr->mode;
37116c5c023SJohan Hovold 
37216c5c023SJohan Hovold 	if (!lm3533->have_backlights && type == LM3533_ATTR_TYPE_BACKLIGHT)
37316c5c023SJohan Hovold 		mode = 0;
37416c5c023SJohan Hovold 	else if (!lm3533->have_leds && type == LM3533_ATTR_TYPE_LED)
37516c5c023SJohan Hovold 		mode = 0;
37616c5c023SJohan Hovold 
37716c5c023SJohan Hovold 	return mode;
37816c5c023SJohan Hovold };
37916c5c023SJohan Hovold 
38016c5c023SJohan Hovold static struct attribute_group lm3533_attribute_group = {
38116c5c023SJohan Hovold 	.is_visible	= lm3533_attr_is_visible,
38216c5c023SJohan Hovold 	.attrs		= lm3533_attributes
38316c5c023SJohan Hovold };
38416c5c023SJohan Hovold 
38516c5c023SJohan Hovold static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
38616c5c023SJohan Hovold {
38716c5c023SJohan Hovold 	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
38816c5c023SJohan Hovold 	int ret;
38916c5c023SJohan Hovold 
39016c5c023SJohan Hovold 	if (!pdata->als)
39116c5c023SJohan Hovold 		return 0;
39216c5c023SJohan Hovold 
39316c5c023SJohan Hovold 	lm3533_als_devs[0].platform_data = pdata->als;
39416c5c023SJohan Hovold 	lm3533_als_devs[0].pdata_size = sizeof(*pdata->als);
39516c5c023SJohan Hovold 
3960848c94fSMark Brown 	ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL,
3970848c94fSMark Brown 			      0, NULL);
39816c5c023SJohan Hovold 	if (ret) {
39916c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to add ALS device\n");
40016c5c023SJohan Hovold 		return ret;
40116c5c023SJohan Hovold 	}
40216c5c023SJohan Hovold 
40316c5c023SJohan Hovold 	lm3533->have_als = 1;
40416c5c023SJohan Hovold 
40516c5c023SJohan Hovold 	return 0;
40616c5c023SJohan Hovold }
40716c5c023SJohan Hovold 
40816c5c023SJohan Hovold static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
40916c5c023SJohan Hovold {
41016c5c023SJohan Hovold 	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
41116c5c023SJohan Hovold 	int i;
41216c5c023SJohan Hovold 	int ret;
41316c5c023SJohan Hovold 
41416c5c023SJohan Hovold 	if (!pdata->backlights || pdata->num_backlights == 0)
41516c5c023SJohan Hovold 		return 0;
41616c5c023SJohan Hovold 
41716c5c023SJohan Hovold 	if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs))
41816c5c023SJohan Hovold 		pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs);
41916c5c023SJohan Hovold 
42016c5c023SJohan Hovold 	for (i = 0; i < pdata->num_backlights; ++i) {
42116c5c023SJohan Hovold 		lm3533_bl_devs[i].platform_data = &pdata->backlights[i];
42216c5c023SJohan Hovold 		lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]);
42316c5c023SJohan Hovold 	}
42416c5c023SJohan Hovold 
42516c5c023SJohan Hovold 	ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs,
4260848c94fSMark Brown 			      pdata->num_backlights, NULL, 0, NULL);
42716c5c023SJohan Hovold 	if (ret) {
42816c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to add backlight devices\n");
42916c5c023SJohan Hovold 		return ret;
43016c5c023SJohan Hovold 	}
43116c5c023SJohan Hovold 
43216c5c023SJohan Hovold 	lm3533->have_backlights = 1;
43316c5c023SJohan Hovold 
43416c5c023SJohan Hovold 	return 0;
43516c5c023SJohan Hovold }
43616c5c023SJohan Hovold 
43716c5c023SJohan Hovold static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
43816c5c023SJohan Hovold {
43916c5c023SJohan Hovold 	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
44016c5c023SJohan Hovold 	int i;
44116c5c023SJohan Hovold 	int ret;
44216c5c023SJohan Hovold 
44316c5c023SJohan Hovold 	if (!pdata->leds || pdata->num_leds == 0)
44416c5c023SJohan Hovold 		return 0;
44516c5c023SJohan Hovold 
44616c5c023SJohan Hovold 	if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs))
44716c5c023SJohan Hovold 		pdata->num_leds = ARRAY_SIZE(lm3533_led_devs);
44816c5c023SJohan Hovold 
44916c5c023SJohan Hovold 	for (i = 0; i < pdata->num_leds; ++i) {
45016c5c023SJohan Hovold 		lm3533_led_devs[i].platform_data = &pdata->leds[i];
45116c5c023SJohan Hovold 		lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]);
45216c5c023SJohan Hovold 	}
45316c5c023SJohan Hovold 
45416c5c023SJohan Hovold 	ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs,
4550848c94fSMark Brown 			      pdata->num_leds, NULL, 0, NULL);
45616c5c023SJohan Hovold 	if (ret) {
45716c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to add LED devices\n");
45816c5c023SJohan Hovold 		return ret;
45916c5c023SJohan Hovold 	}
46016c5c023SJohan Hovold 
46116c5c023SJohan Hovold 	lm3533->have_leds = 1;
46216c5c023SJohan Hovold 
46316c5c023SJohan Hovold 	return 0;
46416c5c023SJohan Hovold }
46516c5c023SJohan Hovold 
466d9055dc5SJohan Hovold static int __devinit lm3533_device_setup(struct lm3533 *lm3533,
467d9055dc5SJohan Hovold 					struct lm3533_platform_data *pdata)
468d9055dc5SJohan Hovold {
469d9055dc5SJohan Hovold 	int ret;
470d9055dc5SJohan Hovold 
471d9055dc5SJohan Hovold 	ret = lm3533_set_boost_freq(lm3533, pdata->boost_freq);
472d9055dc5SJohan Hovold 	if (ret)
473d9055dc5SJohan Hovold 		return ret;
474d9055dc5SJohan Hovold 
475d9055dc5SJohan Hovold 	ret = lm3533_set_boost_ovp(lm3533, pdata->boost_ovp);
476d9055dc5SJohan Hovold 	if (ret)
477d9055dc5SJohan Hovold 		return ret;
478d9055dc5SJohan Hovold 
479d9055dc5SJohan Hovold 	return 0;
480d9055dc5SJohan Hovold }
481d9055dc5SJohan Hovold 
48216c5c023SJohan Hovold static int __devinit lm3533_device_init(struct lm3533 *lm3533)
48316c5c023SJohan Hovold {
48416c5c023SJohan Hovold 	struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
48516c5c023SJohan Hovold 	int ret;
48616c5c023SJohan Hovold 
48716c5c023SJohan Hovold 	dev_dbg(lm3533->dev, "%s\n", __func__);
48816c5c023SJohan Hovold 
48916c5c023SJohan Hovold 	if (!pdata) {
49016c5c023SJohan Hovold 		dev_err(lm3533->dev, "no platform data\n");
49116c5c023SJohan Hovold 		return -EINVAL;
49216c5c023SJohan Hovold 	}
49316c5c023SJohan Hovold 
49416c5c023SJohan Hovold 	lm3533->gpio_hwen = pdata->gpio_hwen;
49516c5c023SJohan Hovold 
49616c5c023SJohan Hovold 	dev_set_drvdata(lm3533->dev, lm3533);
49716c5c023SJohan Hovold 
49816c5c023SJohan Hovold 	if (gpio_is_valid(lm3533->gpio_hwen)) {
49916c5c023SJohan Hovold 		ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW,
50016c5c023SJohan Hovold 								"lm3533-hwen");
50116c5c023SJohan Hovold 		if (ret < 0) {
50216c5c023SJohan Hovold 			dev_err(lm3533->dev,
50316c5c023SJohan Hovold 				"failed to request HWEN GPIO %d\n",
50416c5c023SJohan Hovold 				lm3533->gpio_hwen);
50516c5c023SJohan Hovold 			return ret;
50616c5c023SJohan Hovold 		}
50716c5c023SJohan Hovold 	}
50816c5c023SJohan Hovold 
50916c5c023SJohan Hovold 	lm3533_enable(lm3533);
51016c5c023SJohan Hovold 
511d9055dc5SJohan Hovold 	ret = lm3533_device_setup(lm3533, pdata);
512d9055dc5SJohan Hovold 	if (ret)
513d9055dc5SJohan Hovold 		goto err_disable;
514d9055dc5SJohan Hovold 
51516c5c023SJohan Hovold 	lm3533_device_als_init(lm3533);
51616c5c023SJohan Hovold 	lm3533_device_bl_init(lm3533);
51716c5c023SJohan Hovold 	lm3533_device_led_init(lm3533);
51816c5c023SJohan Hovold 
51916c5c023SJohan Hovold 	ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group);
52016c5c023SJohan Hovold 	if (ret < 0) {
52116c5c023SJohan Hovold 		dev_err(lm3533->dev, "failed to create sysfs attributes\n");
52216c5c023SJohan Hovold 		goto err_unregister;
52316c5c023SJohan Hovold 	}
52416c5c023SJohan Hovold 
52516c5c023SJohan Hovold 	return 0;
52616c5c023SJohan Hovold 
52716c5c023SJohan Hovold err_unregister:
52816c5c023SJohan Hovold 	mfd_remove_devices(lm3533->dev);
529d9055dc5SJohan Hovold err_disable:
53016c5c023SJohan Hovold 	lm3533_disable(lm3533);
53116c5c023SJohan Hovold 	if (gpio_is_valid(lm3533->gpio_hwen))
53216c5c023SJohan Hovold 		gpio_free(lm3533->gpio_hwen);
53316c5c023SJohan Hovold 
53416c5c023SJohan Hovold 	return ret;
53516c5c023SJohan Hovold }
53616c5c023SJohan Hovold 
53716c5c023SJohan Hovold static void __devexit lm3533_device_exit(struct lm3533 *lm3533)
53816c5c023SJohan Hovold {
53916c5c023SJohan Hovold 	dev_dbg(lm3533->dev, "%s\n", __func__);
54016c5c023SJohan Hovold 
54116c5c023SJohan Hovold 	sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group);
54216c5c023SJohan Hovold 
54316c5c023SJohan Hovold 	mfd_remove_devices(lm3533->dev);
54416c5c023SJohan Hovold 	lm3533_disable(lm3533);
54516c5c023SJohan Hovold 	if (gpio_is_valid(lm3533->gpio_hwen))
54616c5c023SJohan Hovold 		gpio_free(lm3533->gpio_hwen);
54716c5c023SJohan Hovold }
54816c5c023SJohan Hovold 
54916c5c023SJohan Hovold static bool lm3533_readable_register(struct device *dev, unsigned int reg)
55016c5c023SJohan Hovold {
55116c5c023SJohan Hovold 	switch (reg) {
55216c5c023SJohan Hovold 	case 0x10 ... 0x2c:
55316c5c023SJohan Hovold 	case 0x30 ... 0x38:
55416c5c023SJohan Hovold 	case 0x40 ... 0x45:
55516c5c023SJohan Hovold 	case 0x50 ... 0x57:
55616c5c023SJohan Hovold 	case 0x60 ... 0x6e:
55716c5c023SJohan Hovold 	case 0x70 ... 0x75:
55816c5c023SJohan Hovold 	case 0x80 ... 0x85:
55916c5c023SJohan Hovold 	case 0x90 ... 0x95:
56016c5c023SJohan Hovold 	case 0xa0 ... 0xa5:
56116c5c023SJohan Hovold 	case 0xb0 ... 0xb2:
56216c5c023SJohan Hovold 		return true;
56316c5c023SJohan Hovold 	default:
56416c5c023SJohan Hovold 		return false;
56516c5c023SJohan Hovold 	}
56616c5c023SJohan Hovold }
56716c5c023SJohan Hovold 
56816c5c023SJohan Hovold static bool lm3533_volatile_register(struct device *dev, unsigned int reg)
56916c5c023SJohan Hovold {
57016c5c023SJohan Hovold 	switch (reg) {
571c48bf153SJohan Hovold 	case 0x34 ... 0x36:	/* zone */
57216c5c023SJohan Hovold 	case 0x37 ... 0x38:	/* adc */
57316c5c023SJohan Hovold 	case 0xb0 ... 0xb1:	/* fault */
57416c5c023SJohan Hovold 		return true;
57516c5c023SJohan Hovold 	default:
57616c5c023SJohan Hovold 		return false;
57716c5c023SJohan Hovold 	}
57816c5c023SJohan Hovold }
57916c5c023SJohan Hovold 
58016c5c023SJohan Hovold static bool lm3533_precious_register(struct device *dev, unsigned int reg)
58116c5c023SJohan Hovold {
58216c5c023SJohan Hovold 	switch (reg) {
58316c5c023SJohan Hovold 	case 0x34:		/* zone */
58416c5c023SJohan Hovold 		return true;
58516c5c023SJohan Hovold 	default:
58616c5c023SJohan Hovold 		return false;
58716c5c023SJohan Hovold 	}
58816c5c023SJohan Hovold }
58916c5c023SJohan Hovold 
59016c5c023SJohan Hovold static struct regmap_config regmap_config = {
59116c5c023SJohan Hovold 	.reg_bits	= 8,
59216c5c023SJohan Hovold 	.val_bits	= 8,
59316c5c023SJohan Hovold 	.max_register	= LM3533_REG_MAX,
59416c5c023SJohan Hovold 	.readable_reg	= lm3533_readable_register,
59516c5c023SJohan Hovold 	.volatile_reg	= lm3533_volatile_register,
59616c5c023SJohan Hovold 	.precious_reg	= lm3533_precious_register,
59716c5c023SJohan Hovold };
59816c5c023SJohan Hovold 
59916c5c023SJohan Hovold static int __devinit lm3533_i2c_probe(struct i2c_client *i2c,
60016c5c023SJohan Hovold 					const struct i2c_device_id *id)
60116c5c023SJohan Hovold {
60216c5c023SJohan Hovold 	struct lm3533 *lm3533;
60316c5c023SJohan Hovold 	int ret;
60416c5c023SJohan Hovold 
60516c5c023SJohan Hovold 	dev_dbg(&i2c->dev, "%s\n", __func__);
60616c5c023SJohan Hovold 
607fa648e51SJohan Hovold 	lm3533 = devm_kzalloc(&i2c->dev, sizeof(*lm3533), GFP_KERNEL);
60816c5c023SJohan Hovold 	if (!lm3533)
60916c5c023SJohan Hovold 		return -ENOMEM;
61016c5c023SJohan Hovold 
61116c5c023SJohan Hovold 	i2c_set_clientdata(i2c, lm3533);
61216c5c023SJohan Hovold 
613fa648e51SJohan Hovold 	lm3533->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
614fa648e51SJohan Hovold 	if (IS_ERR(lm3533->regmap))
615fa648e51SJohan Hovold 		return PTR_ERR(lm3533->regmap);
61616c5c023SJohan Hovold 
61716c5c023SJohan Hovold 	lm3533->dev = &i2c->dev;
61816c5c023SJohan Hovold 	lm3533->irq = i2c->irq;
61916c5c023SJohan Hovold 
62016c5c023SJohan Hovold 	ret = lm3533_device_init(lm3533);
62116c5c023SJohan Hovold 	if (ret)
622fa648e51SJohan Hovold 		return ret;
62316c5c023SJohan Hovold 
62416c5c023SJohan Hovold 	return 0;
62516c5c023SJohan Hovold }
62616c5c023SJohan Hovold 
62716c5c023SJohan Hovold static int __devexit lm3533_i2c_remove(struct i2c_client *i2c)
62816c5c023SJohan Hovold {
62916c5c023SJohan Hovold 	struct lm3533 *lm3533 = i2c_get_clientdata(i2c);
63016c5c023SJohan Hovold 
63116c5c023SJohan Hovold 	dev_dbg(&i2c->dev, "%s\n", __func__);
63216c5c023SJohan Hovold 
63316c5c023SJohan Hovold 	lm3533_device_exit(lm3533);
63416c5c023SJohan Hovold 
63516c5c023SJohan Hovold 	return 0;
63616c5c023SJohan Hovold }
63716c5c023SJohan Hovold 
63816c5c023SJohan Hovold static const struct i2c_device_id lm3533_i2c_ids[] = {
63916c5c023SJohan Hovold 	{ "lm3533", 0 },
64016c5c023SJohan Hovold 	{ },
64116c5c023SJohan Hovold };
64216c5c023SJohan Hovold MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids);
64316c5c023SJohan Hovold 
64416c5c023SJohan Hovold static struct i2c_driver lm3533_i2c_driver = {
64516c5c023SJohan Hovold 	.driver = {
64616c5c023SJohan Hovold 		   .name = "lm3533",
64716c5c023SJohan Hovold 		   .owner = THIS_MODULE,
64816c5c023SJohan Hovold 	},
64916c5c023SJohan Hovold 	.id_table	= lm3533_i2c_ids,
65016c5c023SJohan Hovold 	.probe		= lm3533_i2c_probe,
65116c5c023SJohan Hovold 	.remove		= __devexit_p(lm3533_i2c_remove),
65216c5c023SJohan Hovold };
65316c5c023SJohan Hovold 
65416c5c023SJohan Hovold static int __init lm3533_i2c_init(void)
65516c5c023SJohan Hovold {
65616c5c023SJohan Hovold 	return i2c_add_driver(&lm3533_i2c_driver);
65716c5c023SJohan Hovold }
65816c5c023SJohan Hovold subsys_initcall(lm3533_i2c_init);
65916c5c023SJohan Hovold 
66016c5c023SJohan Hovold static void __exit lm3533_i2c_exit(void)
66116c5c023SJohan Hovold {
66216c5c023SJohan Hovold 	i2c_del_driver(&lm3533_i2c_driver);
66316c5c023SJohan Hovold }
66416c5c023SJohan Hovold module_exit(lm3533_i2c_exit);
66516c5c023SJohan Hovold 
66616c5c023SJohan Hovold MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
66716c5c023SJohan Hovold MODULE_DESCRIPTION("LM3533 Core");
66816c5c023SJohan Hovold MODULE_LICENSE("GPL");
669