Blame view

drivers/mfd/88pm805.c 6.38 KB
70c6cce04   Qiao Zhou   mfd: Support 88pm...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  /*
   * Base driver for Marvell 88PM805
   *
   * Copyright (C) 2012 Marvell International Ltd.
   * Haojian Zhuang <haojian.zhuang@marvell.com>
   * Joseph(Yossi) Hanin <yhanin@marvell.com>
   * Qiao Zhou <zhouqiao@marvell.com>
   *
   * This file is subject to the terms and conditions of the GNU General
   * Public License. See the file "COPYING" in the main directory of this
   * archive for more details.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/i2c.h>
  #include <linux/irq.h>
  #include <linux/mfd/core.h>
  #include <linux/mfd/88pm80x.h>
  #include <linux/slab.h>
  #include <linux/delay.h>
70c6cce04   Qiao Zhou   mfd: Support 88pm...
31
  static const struct i2c_device_id pm80x_id_table[] = {
03dcc544b   Chao Xie   mfd: 88pm80x: Cha...
32
  	{"88PM805", 0},
31b3ffbdf   Samuel Ortiz   mfd: 88pm80[05] i...
33
  	{} /* NULL terminated */
70c6cce04   Qiao Zhou   mfd: Support 88pm...
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
  };
  MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
  
  /* Interrupt Number in 88PM805 */
  enum {
  	PM805_IRQ_LDO_OFF,	/*0 */
  	PM805_IRQ_SRC_DPLL_LOCK,	/*1 */
  	PM805_IRQ_CLIP_FAULT,
  	PM805_IRQ_MIC_CONFLICT,
  	PM805_IRQ_HP2_SHRT,
  	PM805_IRQ_HP1_SHRT,	/*5 */
  	PM805_IRQ_FINE_PLL_FAULT,
  	PM805_IRQ_RAW_PLL_FAULT,
  	PM805_IRQ_VOLP_BTN_DET,
  	PM805_IRQ_VOLM_BTN_DET,
  	PM805_IRQ_SHRT_BTN_DET,	/*10 */
  	PM805_IRQ_MIC_DET,	/*11 */
  
  	PM805_MAX_IRQ,
  };
  
  static struct resource codec_resources[] = {
  	{
  	 /* Headset microphone insertion or removal */
  	 .name = "micin",
  	 .start = PM805_IRQ_MIC_DET,
  	 .end = PM805_IRQ_MIC_DET,
  	 .flags = IORESOURCE_IRQ,
  	 },
  	{
  	 /* Audio short HP1 */
  	 .name = "audio-short1",
  	 .start = PM805_IRQ_HP1_SHRT,
  	 .end = PM805_IRQ_HP1_SHRT,
  	 .flags = IORESOURCE_IRQ,
  	 },
  	{
  	 /* Audio short HP2 */
  	 .name = "audio-short2",
  	 .start = PM805_IRQ_HP2_SHRT,
  	 .end = PM805_IRQ_HP2_SHRT,
  	 .flags = IORESOURCE_IRQ,
  	 },
  };
04e024173   Geert Uytterhoeven   mfd: marvell: Con...
78
  static const struct mfd_cell codec_devs[] = {
70c6cce04   Qiao Zhou   mfd: Support 88pm...
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  	{
  	 .name = "88pm80x-codec",
  	 .num_resources = ARRAY_SIZE(codec_resources),
  	 .resources = &codec_resources[0],
  	 .id = -1,
  	 },
  };
  
  static struct regmap_irq pm805_irqs[] = {
  	/* INT0 */
  	[PM805_IRQ_LDO_OFF] = {
  		.mask = PM805_INT1_HP1_SHRT,
  	},
  	[PM805_IRQ_SRC_DPLL_LOCK] = {
  		.mask = PM805_INT1_HP2_SHRT,
  	},
  	[PM805_IRQ_CLIP_FAULT] = {
  		.mask = PM805_INT1_MIC_CONFLICT,
  	},
  	[PM805_IRQ_MIC_CONFLICT] = {
  		.mask = PM805_INT1_CLIP_FAULT,
  	},
  	[PM805_IRQ_HP2_SHRT] = {
  		.mask = PM805_INT1_LDO_OFF,
  	},
  	[PM805_IRQ_HP1_SHRT] = {
  		.mask = PM805_INT1_SRC_DPLL_LOCK,
  	},
  	/* INT1 */
  	[PM805_IRQ_FINE_PLL_FAULT] = {
  		.reg_offset = 1,
  		.mask = PM805_INT2_MIC_DET,
  	},
  	[PM805_IRQ_RAW_PLL_FAULT] = {
  		.reg_offset = 1,
  		.mask = PM805_INT2_SHRT_BTN_DET,
  	},
  	[PM805_IRQ_VOLP_BTN_DET] = {
  		.reg_offset = 1,
  		.mask = PM805_INT2_VOLM_BTN_DET,
  	},
  	[PM805_IRQ_VOLM_BTN_DET] = {
  		.reg_offset = 1,
  		.mask = PM805_INT2_VOLP_BTN_DET,
  	},
  	[PM805_IRQ_SHRT_BTN_DET] = {
  		.reg_offset = 1,
  		.mask = PM805_INT2_RAW_PLL_FAULT,
  	},
  	[PM805_IRQ_MIC_DET] = {
  		.reg_offset = 1,
  		.mask = PM805_INT2_FINE_PLL_FAULT,
  	},
  };
f791be492   Bill Pemberton   mfd: remove use o...
133
  static int device_irq_init_805(struct pm80x_chip *chip)
70c6cce04   Qiao Zhou   mfd: Support 88pm...
134
135
  {
  	struct regmap *map = chip->regmap;
1ef5677e0   Yi Zhang   mfd: 88pm800: 88p...
136
  	unsigned long flags = IRQF_ONESHOT;
70c6cce04   Qiao Zhou   mfd: Support 88pm...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  	int data, mask, ret = -EINVAL;
  
  	if (!map || !chip->irq) {
  		dev_err(chip->dev, "incorrect parameters
  ");
  		return -EINVAL;
  	}
  
  	/*
  	 * irq_mode defines the way of clearing interrupt. it's read-clear by
  	 * default.
  	 */
  	mask =
  	    PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT |
  	    PM800_STATUS0_INT_MASK;
  
  	data = PM805_STATUS0_INT_CLEAR;
  	ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data);
  	/*
  	 * PM805_INT_STATUS is under 32K clock domain, so need to
  	 * add proper delay before the next I2C register access.
  	 */
dc5439292   Lee Jones   mfd: 88pm805: msl...
159
  	usleep_range(1000, 3000);
70c6cce04   Qiao Zhou   mfd: Support 88pm...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  
  	if (ret < 0)
  		goto out;
  
  	ret =
  	    regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
  				chip->regmap_irq_chip, &chip->irq_data);
  
  out:
  	return ret;
  }
  
  static void device_irq_exit_805(struct pm80x_chip *chip)
  {
  	regmap_del_irq_chip(chip->irq, chip->irq_data);
  }
  
  static struct regmap_irq_chip pm805_irq_chip = {
  	.name = "88pm805",
  	.irqs = pm805_irqs,
  	.num_irqs = ARRAY_SIZE(pm805_irqs),
  
  	.num_regs = 2,
  	.status_base = PM805_INT_STATUS1,
  	.mask_base = PM805_INT_MASK1,
  	.ack_base = PM805_INT_STATUS1,
  };
f791be492   Bill Pemberton   mfd: remove use o...
187
  static int device_805_init(struct pm80x_chip *chip)
70c6cce04   Qiao Zhou   mfd: Support 88pm...
188
189
190
191
192
193
194
195
196
  {
  	int ret = 0;
  	struct regmap *map = chip->regmap;
  
  	if (!map) {
  		dev_err(chip->dev, "regmap is invalid
  ");
  		return -EINVAL;
  	}
70c6cce04   Qiao Zhou   mfd: Support 88pm...
197
198
199
200
201
202
203
204
205
206
  	chip->regmap_irq_chip = &pm805_irq_chip;
  
  	ret = device_irq_init_805(chip);
  	if (ret < 0) {
  		dev_err(chip->dev, "Failed to init pm805 irq!
  ");
  		goto out_irq_init;
  	}
  
  	ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
0848c94fb   Mark Brown   mfd: core: Push i...
207
208
  			      ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
  			      NULL);
70c6cce04   Qiao Zhou   mfd: Support 88pm...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  	if (ret < 0) {
  		dev_err(chip->dev, "Failed to add codec subdev
  ");
  		goto out_codec;
  	} else
  		dev_info(chip->dev, "[%s]:Added mfd codec_devs
  ", __func__);
  
  	return 0;
  
  out_codec:
  	device_irq_exit_805(chip);
  out_irq_init:
  	return ret;
  }
f791be492   Bill Pemberton   mfd: remove use o...
224
  static int pm805_probe(struct i2c_client *client,
70c6cce04   Qiao Zhou   mfd: Support 88pm...
225
226
227
228
  				 const struct i2c_device_id *id)
  {
  	int ret = 0;
  	struct pm80x_chip *chip;
334a41ce9   Jingoo Han   mfd: Use dev_get_...
229
  	struct pm80x_platform_data *pdata = dev_get_platdata(&client->dev);
70c6cce04   Qiao Zhou   mfd: Support 88pm...
230

03dcc544b   Chao Xie   mfd: 88pm80x: Cha...
231
  	ret = pm80x_init(client);
70c6cce04   Qiao Zhou   mfd: Support 88pm...
232
233
234
235
236
237
238
239
240
241
  	if (ret) {
  		dev_err(&client->dev, "pm805_init fail!
  ");
  		goto out_init;
  	}
  
  	chip = i2c_get_clientdata(client);
  
  	ret = device_805_init(chip);
  	if (ret) {
03dcc544b   Chao Xie   mfd: 88pm80x: Cha...
242
243
  		dev_err(chip->dev, "Failed to initialize 88pm805 devices
  ");
70c6cce04   Qiao Zhou   mfd: Support 88pm...
244
245
  		goto err_805_init;
  	}
2b274fe52   Chao Xie   mfd: 88pm805: Fix...
246
  	if (pdata && pdata->plat_config)
70c6cce04   Qiao Zhou   mfd: Support 88pm...
247
248
249
  		pdata->plat_config(chip, pdata);
  
  err_805_init:
306df7985   Yi Zhang   mfd: 88pm80x: Rem...
250
  	pm80x_deinit();
70c6cce04   Qiao Zhou   mfd: Support 88pm...
251
252
253
  out_init:
  	return ret;
  }
4740f73fe   Bill Pemberton   mfd: remove use o...
254
  static int pm805_remove(struct i2c_client *client)
70c6cce04   Qiao Zhou   mfd: Support 88pm...
255
256
257
258
259
  {
  	struct pm80x_chip *chip = i2c_get_clientdata(client);
  
  	mfd_remove_devices(chip->dev);
  	device_irq_exit_805(chip);
306df7985   Yi Zhang   mfd: 88pm80x: Rem...
260
  	pm80x_deinit();
70c6cce04   Qiao Zhou   mfd: Support 88pm...
261
262
263
264
265
266
  
  	return 0;
  }
  
  static struct i2c_driver pm805_driver = {
  	.driver = {
46223a19a   Chao Xie   mfd: 88pm80x: Fix...
267
  		.name = "88PM805",
70c6cce04   Qiao Zhou   mfd: Support 88pm...
268
269
270
  		.pm = &pm80x_pm_ops,
  		},
  	.probe = pm805_probe,
84449216b   Bill Pemberton   mfd: remove use o...
271
  	.remove = pm805_remove,
70c6cce04   Qiao Zhou   mfd: Support 88pm...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  	.id_table = pm80x_id_table,
  };
  
  static int __init pm805_i2c_init(void)
  {
  	return i2c_add_driver(&pm805_driver);
  }
  subsys_initcall(pm805_i2c_init);
  
  static void __exit pm805_i2c_exit(void)
  {
  	i2c_del_driver(&pm805_driver);
  }
  module_exit(pm805_i2c_exit);
  
  MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805");
  MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
  MODULE_LICENSE("GPL");