Blame view

drivers/mfd/as3711.c 5.27 KB
aecd8454c   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
2
3
4
5
6
  /*
   * AS3711 PMIC MFC driver
   *
   * Copyright (C) 2012 Renesas Electronics Corporation
   * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
7
8
9
10
11
12
13
14
15
   */
  
  #include <linux/device.h>
  #include <linux/err.h>
  #include <linux/i2c.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/mfd/as3711.h>
  #include <linux/mfd/core.h>
0af6f271d   Sachin Kamat   mfd: as3711: Incl...
16
  #include <linux/of.h>
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  #include <linux/regmap.h>
  #include <linux/slab.h>
  
  enum {
  	AS3711_REGULATOR,
  	AS3711_BACKLIGHT,
  };
  
  /*
   * Ok to have it static: it is only used during probing and multiple I2C devices
   * cannot be probed simultaneously. Just make sure to avoid stale data.
   */
  static struct mfd_cell as3711_subdevs[] = {
  	[AS3711_REGULATOR] = {.name = "as3711-regulator",},
  	[AS3711_BACKLIGHT] = {.name = "as3711-backlight",},
  };
  
  static bool as3711_volatile_reg(struct device *dev, unsigned int reg)
  {
  	switch (reg) {
  	case AS3711_GPIO_SIGNAL_IN:
  	case AS3711_INTERRUPT_STATUS_1:
  	case AS3711_INTERRUPT_STATUS_2:
  	case AS3711_INTERRUPT_STATUS_3:
  	case AS3711_CHARGER_STATUS_1:
  	case AS3711_CHARGER_STATUS_2:
  	case AS3711_REG_STATUS:
  		return true;
  	}
  	return false;
  }
  
  static bool as3711_precious_reg(struct device *dev, unsigned int reg)
  {
  	switch (reg) {
  	case AS3711_INTERRUPT_STATUS_1:
  	case AS3711_INTERRUPT_STATUS_2:
  	case AS3711_INTERRUPT_STATUS_3:
  		return true;
  	}
  	return false;
  }
  
  static bool as3711_readable_reg(struct device *dev, unsigned int reg)
  {
  	switch (reg) {
  	case AS3711_SD_1_VOLTAGE:
  	case AS3711_SD_2_VOLTAGE:
  	case AS3711_SD_3_VOLTAGE:
  	case AS3711_SD_4_VOLTAGE:
  	case AS3711_LDO_1_VOLTAGE:
  	case AS3711_LDO_2_VOLTAGE:
  	case AS3711_LDO_3_VOLTAGE:
  	case AS3711_LDO_4_VOLTAGE:
  	case AS3711_LDO_5_VOLTAGE:
  	case AS3711_LDO_6_VOLTAGE:
  	case AS3711_LDO_7_VOLTAGE:
  	case AS3711_LDO_8_VOLTAGE:
  	case AS3711_SD_CONTROL:
  	case AS3711_GPIO_SIGNAL_OUT:
  	case AS3711_GPIO_SIGNAL_IN:
  	case AS3711_SD_CONTROL_1:
  	case AS3711_SD_CONTROL_2:
  	case AS3711_CURR_CONTROL:
  	case AS3711_CURR1_VALUE:
  	case AS3711_CURR2_VALUE:
  	case AS3711_CURR3_VALUE:
  	case AS3711_STEPUP_CONTROL_1:
  	case AS3711_STEPUP_CONTROL_2:
  	case AS3711_STEPUP_CONTROL_4:
  	case AS3711_STEPUP_CONTROL_5:
  	case AS3711_REG_STATUS:
  	case AS3711_INTERRUPT_STATUS_1:
  	case AS3711_INTERRUPT_STATUS_2:
  	case AS3711_INTERRUPT_STATUS_3:
  	case AS3711_CHARGER_STATUS_1:
  	case AS3711_CHARGER_STATUS_2:
  	case AS3711_ASIC_ID_1:
  	case AS3711_ASIC_ID_2:
  		return true;
  	}
  	return false;
  }
  
  static const struct regmap_config as3711_regmap_config = {
  	.reg_bits = 8,
  	.val_bits = 8,
  	.volatile_reg = as3711_volatile_reg,
  	.readable_reg = as3711_readable_reg,
  	.precious_reg = as3711_precious_reg,
e9b7ba795   Maciej S. Szmigiero   mfd: as3711: Set ...
107
108
  	.max_register = AS3711_MAX_REG,
  	.num_reg_defaults_raw = AS3711_NUM_REGS,
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
109
110
  	.cache_type = REGCACHE_RBTREE,
  };
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
111
  #ifdef CONFIG_OF
445603030   Krzysztof Kozlowski   mfd: as3711: Make...
112
  static const struct of_device_id as3711_of_match[] = {
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
113
114
115
  	{.compatible = "ams,as3711",},
  	{}
  };
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
116
  #endif
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
117
118
119
120
  static int as3711_i2c_probe(struct i2c_client *client,
  			    const struct i2c_device_id *id)
  {
  	struct as3711 *as3711;
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
121
  	struct as3711_platform_data *pdata;
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
122
123
  	unsigned int id1, id2;
  	int ret;
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
124
  	if (!client->dev.of_node) {
334a41ce9   Jingoo Han   mfd: Use dev_get_...
125
  		pdata = dev_get_platdata(&client->dev);
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
126
127
128
129
130
131
  		if (!pdata)
  			dev_dbg(&client->dev, "Platform data not found
  ");
  	} else {
  		pdata = devm_kzalloc(&client->dev,
  				     sizeof(*pdata), GFP_KERNEL);
ae487ae2a   Lee Jones   mfd: as3711: Repa...
132
  		if (!pdata)
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
133
  			return -ENOMEM;
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
134
  	}
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
135
136
  
  	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
ae487ae2a   Lee Jones   mfd: as3711: Repa...
137
  	if (!as3711)
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
138
  		return -ENOMEM;
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
139
140
141
142
143
144
145
146
147
148
149
  
  	as3711->dev = &client->dev;
  	i2c_set_clientdata(client, as3711);
  
  	if (client->irq)
  		dev_notice(&client->dev, "IRQ not supported yet
  ");
  
  	as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config);
  	if (IS_ERR(as3711->regmap)) {
  		ret = PTR_ERR(as3711->regmap);
ae487ae2a   Lee Jones   mfd: as3711: Repa...
150
151
152
  		dev_err(&client->dev,
  			"regmap initialization failed: %d
  ", ret);
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  		return ret;
  	}
  
  	ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1);
  	if (!ret)
  		ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2);
  	if (ret < 0) {
  		dev_err(&client->dev, "regmap_read() failed: %d
  ", ret);
  		return ret;
  	}
  	if (id1 != 0x8b)
  		return -ENODEV;
  	dev_info(as3711->dev, "AS3711 detected: %x:%x
  ", id1, id2);
ae487ae2a   Lee Jones   mfd: as3711: Repa...
168
169
170
171
  	/*
  	 * We can reuse as3711_subdevs[],
  	 * it will be copied in mfd_add_devices()
  	 */
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
172
  	if (pdata) {
ae487ae2a   Lee Jones   mfd: as3711: Repa...
173
174
175
176
177
178
179
180
  		as3711_subdevs[AS3711_REGULATOR].platform_data =
  			&pdata->regulator;
  		as3711_subdevs[AS3711_REGULATOR].pdata_size =
  			sizeof(pdata->regulator);
  		as3711_subdevs[AS3711_BACKLIGHT].platform_data =
  			&pdata->backlight;
  		as3711_subdevs[AS3711_BACKLIGHT].pdata_size =
  			sizeof(pdata->backlight);
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
181
182
183
184
185
186
  	} else {
  		as3711_subdevs[AS3711_REGULATOR].platform_data = NULL;
  		as3711_subdevs[AS3711_REGULATOR].pdata_size = 0;
  		as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL;
  		as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0;
  	}
9c9983267   Laxman Dewangan   mfd: as3711: Use ...
187
188
  	ret = devm_mfd_add_devices(as3711->dev, -1, as3711_subdevs,
  				   ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL);
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
189
190
191
192
193
194
  	if (ret < 0)
  		dev_err(&client->dev, "add mfd devices failed: %d
  ", ret);
  
  	return ret;
  }
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
195
196
197
198
  static const struct i2c_device_id as3711_i2c_id[] = {
  	{.name = "as3711", .driver_data = 0},
  	{}
  };
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
199
200
201
  static struct i2c_driver as3711_i2c_driver = {
  	.driver = {
  		   .name = "as3711",
64710af3e   Guennadi Liakhovetski   mfd: as3711: Add ...
202
203
  		   .of_match_table = of_match_ptr(as3711_of_match),
  	},
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
204
  	.probe = as3711_i2c_probe,
acad189b0   Guennadi Liakhovetski   mfd: Add an AS371...
205
206
207
208
209
210
211
212
213
  	.id_table = as3711_i2c_id,
  };
  
  static int __init as3711_i2c_init(void)
  {
  	return i2c_add_driver(&as3711_i2c_driver);
  }
  /* Initialise early */
  subsys_initcall(as3711_i2c_init);