Blame view

drivers/mfd/max8925-i2c.c 5.88 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
d50f8f339   Haojian Zhuang   mfd: Initial max8...
2
3
4
5
6
  /*
   * I2C driver for Maxim MAX8925
   *
   * Copyright (C) 2009 Marvell International Ltd.
   *	Haojian Zhuang <haojian.zhuang@marvell.com>
d50f8f339   Haojian Zhuang   mfd: Initial max8...
7
8
   */
  #include <linux/kernel.h>
9eb5f9ba1   Paul Gortmaker   mfd: max8925-i2c:...
9
  #include <linux/init.h>
d50f8f339   Haojian Zhuang   mfd: Initial max8...
10
11
12
  #include <linux/platform_device.h>
  #include <linux/i2c.h>
  #include <linux/mfd/max8925.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
d50f8f339   Haojian Zhuang   mfd: Initial max8...
14

b13c0df51   Haojian Zhuang   mfd: Update i2c d...
15
16
  #define RTC_I2C_ADDR		0x68
  #define ADC_I2C_ADDR		0x47
d50f8f339   Haojian Zhuang   mfd: Initial max8...
17
18
19
  static inline int max8925_read_device(struct i2c_client *i2c,
  				      int reg, int bytes, void *dest)
  {
d50f8f339   Haojian Zhuang   mfd: Initial max8...
20
  	int ret;
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
21
22
23
24
25
26
27
28
29
  	if (bytes > 1)
  		ret = i2c_smbus_read_i2c_block_data(i2c, reg, bytes, dest);
  	else {
  		ret = i2c_smbus_read_byte_data(i2c, reg);
  		if (ret < 0)
  			return ret;
  		*(unsigned char *)dest = (unsigned char)ret;
  	}
  	return ret;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
30
31
32
33
34
  }
  
  static inline int max8925_write_device(struct i2c_client *i2c,
  				       int reg, int bytes, void *src)
  {
87bd1c925   Lee Jones   mfd: max8925-i2c:...
35
  	unsigned char buf[9];
d50f8f339   Haojian Zhuang   mfd: Initial max8...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  	int ret;
  
  	buf[0] = (unsigned char)reg;
  	memcpy(&buf[1], src, bytes);
  
  	ret = i2c_master_send(i2c, buf, bytes + 1);
  	if (ret < 0)
  		return ret;
  	return 0;
  }
  
  int max8925_reg_read(struct i2c_client *i2c, int reg)
  {
  	struct max8925_chip *chip = i2c_get_clientdata(i2c);
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
50
  	unsigned char data = 0;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  	int ret;
  
  	mutex_lock(&chip->io_lock);
  	ret = max8925_read_device(i2c, reg, 1, &data);
  	mutex_unlock(&chip->io_lock);
  
  	if (ret < 0)
  		return ret;
  	else
  		return (int)data;
  }
  EXPORT_SYMBOL(max8925_reg_read);
  
  int max8925_reg_write(struct i2c_client *i2c, int reg,
  		unsigned char data)
  {
  	struct max8925_chip *chip = i2c_get_clientdata(i2c);
  	int ret;
  
  	mutex_lock(&chip->io_lock);
  	ret = max8925_write_device(i2c, reg, 1, &data);
  	mutex_unlock(&chip->io_lock);
  
  	return ret;
  }
  EXPORT_SYMBOL(max8925_reg_write);
  
  int max8925_bulk_read(struct i2c_client *i2c, int reg,
  		int count, unsigned char *buf)
  {
  	struct max8925_chip *chip = i2c_get_clientdata(i2c);
  	int ret;
  
  	mutex_lock(&chip->io_lock);
  	ret = max8925_read_device(i2c, reg, count, buf);
  	mutex_unlock(&chip->io_lock);
  
  	return ret;
  }
  EXPORT_SYMBOL(max8925_bulk_read);
  
  int max8925_bulk_write(struct i2c_client *i2c, int reg,
  		int count, unsigned char *buf)
  {
  	struct max8925_chip *chip = i2c_get_clientdata(i2c);
  	int ret;
  
  	mutex_lock(&chip->io_lock);
  	ret = max8925_write_device(i2c, reg, count, buf);
  	mutex_unlock(&chip->io_lock);
  
  	return ret;
  }
  EXPORT_SYMBOL(max8925_bulk_write);
  
  int max8925_set_bits(struct i2c_client *i2c, int reg,
  		unsigned char mask, unsigned char data)
  {
  	struct max8925_chip *chip = i2c_get_clientdata(i2c);
  	unsigned char value;
  	int ret;
  
  	mutex_lock(&chip->io_lock);
  	ret = max8925_read_device(i2c, reg, 1, &value);
  	if (ret < 0)
  		goto out;
  	value &= ~mask;
  	value |= data;
  	ret = max8925_write_device(i2c, reg, 1, &value);
  out:
  	mutex_unlock(&chip->io_lock);
  	return ret;
  }
  EXPORT_SYMBOL(max8925_set_bits);
  
  
  static const struct i2c_device_id max8925_id_table[] = {
  	{ "max8925", 0 },
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
129
  	{ },
d50f8f339   Haojian Zhuang   mfd: Initial max8...
130
  };
d50f8f339   Haojian Zhuang   mfd: Initial max8...
131

4e405ae25   Qing Xu   mfd: max8925: Add...
132
133
134
135
136
137
138
139
140
141
142
143
144
  static int max8925_dt_init(struct device_node *np, struct device *dev,
  			   struct max8925_platform_data *pdata)
  {
  	int ret;
  
  	ret = of_property_read_u32(np, "maxim,tsc-irq", &pdata->tsc_irq);
  	if (ret) {
  		dev_err(dev, "Not found maxim,tsc-irq property
  ");
  		return -EINVAL;
  	}
  	return 0;
  }
f791be492   Bill Pemberton   mfd: remove use o...
145
  static int max8925_probe(struct i2c_client *client,
d50f8f339   Haojian Zhuang   mfd: Initial max8...
146
147
  				   const struct i2c_device_id *id)
  {
334a41ce9   Jingoo Han   mfd: Use dev_get_...
148
  	struct max8925_platform_data *pdata = dev_get_platdata(&client->dev);
095307511   Julia Lawall   mfd: max8925-i2c:...
149
  	struct max8925_chip *chip;
4e405ae25   Qing Xu   mfd: max8925: Add...
150
151
152
153
154
155
156
157
158
159
160
161
162
  	struct device_node *node = client->dev.of_node;
  
  	if (node && !pdata) {
  		/* parse DT to get platform data */
  		pdata = devm_kzalloc(&client->dev,
  				     sizeof(struct max8925_platform_data),
  				     GFP_KERNEL);
  		if (!pdata)
  			return -ENOMEM;
  
  		if (max8925_dt_init(node, &client->dev, pdata))
  			return -EINVAL;
  	} else if (!pdata) {
d50f8f339   Haojian Zhuang   mfd: Initial max8...
163
164
165
166
  		pr_info("%s: platform data is missing
  ", __func__);
  		return -EINVAL;
  	}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
167

02c7d8489   Lee Jones   mfd: max8925: Con...
168
169
  	chip = devm_kzalloc(&client->dev,
  			    sizeof(struct max8925_chip), GFP_KERNEL);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
170
171
172
  	if (chip == NULL)
  		return -ENOMEM;
  	chip->i2c = client;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
173
  	chip->dev = &client->dev;
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
174
  	i2c_set_clientdata(client, chip);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
175
  	dev_set_drvdata(chip->dev, chip);
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
176
  	mutex_init(&chip->io_lock);
ddbf6ffeb   Wolfram Sang   mfd: max8925-i2c:...
177
178
  	chip->rtc = i2c_new_dummy_device(chip->i2c->adapter, RTC_I2C_ADDR);
  	if (IS_ERR(chip->rtc)) {
96cf3dedc   Krzysztof Kozlowski   mfd: max8925: Fix...
179
180
  		dev_err(chip->dev, "Failed to allocate I2C device for RTC
  ");
ddbf6ffeb   Wolfram Sang   mfd: max8925-i2c:...
181
  		return PTR_ERR(chip->rtc);
96cf3dedc   Krzysztof Kozlowski   mfd: max8925: Fix...
182
  	}
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
183
  	i2c_set_clientdata(chip->rtc, chip);
ddbf6ffeb   Wolfram Sang   mfd: max8925-i2c:...
184
185
  	chip->adc = i2c_new_dummy_device(chip->i2c->adapter, ADC_I2C_ADDR);
  	if (IS_ERR(chip->adc)) {
96cf3dedc   Krzysztof Kozlowski   mfd: max8925: Fix...
186
187
188
  		dev_err(chip->dev, "Failed to allocate I2C device for ADC
  ");
  		i2c_unregister_device(chip->rtc);
ddbf6ffeb   Wolfram Sang   mfd: max8925-i2c:...
189
  		return PTR_ERR(chip->adc);
96cf3dedc   Krzysztof Kozlowski   mfd: max8925: Fix...
190
  	}
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
191
  	i2c_set_clientdata(chip->adc, chip);
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
192
  	device_init_wakeup(&client->dev, 1);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
193
194
195
196
  	max8925_device_init(chip, pdata);
  
  	return 0;
  }
4740f73fe   Bill Pemberton   mfd: remove use o...
197
  static int max8925_remove(struct i2c_client *client)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
198
199
200
201
  {
  	struct max8925_chip *chip = i2c_get_clientdata(client);
  
  	max8925_device_exit(chip);
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
202
203
  	i2c_unregister_device(chip->adc);
  	i2c_unregister_device(chip->rtc);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
204
205
  	return 0;
  }
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
206
207
208
  #ifdef CONFIG_PM_SLEEP
  static int max8925_suspend(struct device *dev)
  {
1b5420e1f   Geliang Tang   mfd: Use to_i2c_c...
209
  	struct i2c_client *client = to_i2c_client(dev);
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
210
211
212
213
214
215
216
217
218
  	struct max8925_chip *chip = i2c_get_clientdata(client);
  
  	if (device_may_wakeup(dev) && chip->wakeup_flag)
  		enable_irq_wake(chip->core_irq);
  	return 0;
  }
  
  static int max8925_resume(struct device *dev)
  {
1b5420e1f   Geliang Tang   mfd: Use to_i2c_c...
219
  	struct i2c_client *client = to_i2c_client(dev);
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
220
221
222
223
224
225
226
227
228
  	struct max8925_chip *chip = i2c_get_clientdata(client);
  
  	if (device_may_wakeup(dev) && chip->wakeup_flag)
  		disable_irq_wake(chip->core_irq);
  	return 0;
  }
  #endif
  
  static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume);
4e405ae25   Qing Xu   mfd: max8925: Add...
229
230
231
232
  static const struct of_device_id max8925_dt_ids[] = {
  	{ .compatible = "maxim,max8925", },
  	{},
  };
4e405ae25   Qing Xu   mfd: max8925: Add...
233

d50f8f339   Haojian Zhuang   mfd: Initial max8...
234
235
236
  static struct i2c_driver max8925_driver = {
  	.driver	= {
  		.name	= "max8925",
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
237
  		.pm     = &max8925_pm_ops,
52c2c6ebf   Sachin Kamat   mfd: max8925: Rem...
238
  		.of_match_table = max8925_dt_ids,
d50f8f339   Haojian Zhuang   mfd: Initial max8...
239
240
  	},
  	.probe		= max8925_probe,
84449216b   Bill Pemberton   mfd: remove use o...
241
  	.remove		= max8925_remove,
d50f8f339   Haojian Zhuang   mfd: Initial max8...
242
243
244
245
246
247
  	.id_table	= max8925_id_table,
  };
  
  static int __init max8925_i2c_init(void)
  {
  	int ret;
4ed8f7182   Lee Jones   mfd: max8925-i2c:...
248

d50f8f339   Haojian Zhuang   mfd: Initial max8...
249
250
251
252
  	ret = i2c_add_driver(&max8925_driver);
  	if (ret != 0)
  		pr_err("Failed to register MAX8925 I2C driver: %d
  ", ret);
4ed8f7182   Lee Jones   mfd: max8925-i2c:...
253

d50f8f339   Haojian Zhuang   mfd: Initial max8...
254
255
256
  	return ret;
  }
  subsys_initcall(max8925_i2c_init);