Blame view

drivers/mfd/max8925-i2c.c 5.38 KB
d50f8f339   Haojian Zhuang   mfd: Initial max8...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * I2C driver for Maxim MAX8925
   *
   * Copyright (C) 2009 Marvell International Ltd.
   *	Haojian Zhuang <haojian.zhuang@marvell.com>
   *
   * 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 <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/i2c.h>
  #include <linux/mfd/max8925.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
d50f8f339   Haojian Zhuang   mfd: Initial max8...
17

b13c0df51   Haojian Zhuang   mfd: Update i2c d...
18
19
  #define RTC_I2C_ADDR		0x68
  #define ADC_I2C_ADDR		0x47
d50f8f339   Haojian Zhuang   mfd: Initial max8...
20
21
22
  static inline int max8925_read_device(struct i2c_client *i2c,
  				      int reg, int bytes, void *dest)
  {
d50f8f339   Haojian Zhuang   mfd: Initial max8...
23
  	int ret;
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
24
25
26
27
28
29
30
31
32
  	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...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  }
  
  static inline int max8925_write_device(struct i2c_client *i2c,
  				       int reg, int bytes, void *src)
  {
  	unsigned char buf[bytes + 1];
  	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...
53
  	unsigned char data = 0;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
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
129
130
131
  	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...
132
  	{ },
d50f8f339   Haojian Zhuang   mfd: Initial max8...
133
134
135
136
137
138
139
  };
  MODULE_DEVICE_TABLE(i2c, max8925_id_table);
  
  static int __devinit max8925_probe(struct i2c_client *client,
  				   const struct i2c_device_id *id)
  {
  	struct max8925_platform_data *pdata = client->dev.platform_data;
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
140
  	static struct max8925_chip *chip;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
141
142
143
144
145
146
  
  	if (!pdata) {
  		pr_info("%s: platform data is missing
  ", __func__);
  		return -EINVAL;
  	}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
147
148
149
150
151
  
  	chip = kzalloc(sizeof(struct max8925_chip), GFP_KERNEL);
  	if (chip == NULL)
  		return -ENOMEM;
  	chip->i2c = client;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
152
  	chip->dev = &client->dev;
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
153
  	i2c_set_clientdata(client, chip);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
154
  	dev_set_drvdata(chip->dev, chip);
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
155
156
157
158
159
160
161
  	mutex_init(&chip->io_lock);
  
  	chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR);
  	i2c_set_clientdata(chip->rtc, chip);
  
  	chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR);
  	i2c_set_clientdata(chip->adc, chip);
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
162
  	device_init_wakeup(&client->dev, 1);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
163
164
165
166
167
168
169
170
171
172
  	max8925_device_init(chip, pdata);
  
  	return 0;
  }
  
  static int __devexit max8925_remove(struct i2c_client *client)
  {
  	struct max8925_chip *chip = i2c_get_clientdata(client);
  
  	max8925_device_exit(chip);
b13c0df51   Haojian Zhuang   mfd: Update i2c d...
173
174
  	i2c_unregister_device(chip->adc);
  	i2c_unregister_device(chip->rtc);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
175
176
177
  	kfree(chip);
  	return 0;
  }
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  #ifdef CONFIG_PM_SLEEP
  static int max8925_suspend(struct device *dev)
  {
  	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
  	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)
  {
  	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
  	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);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
201
202
203
204
  static struct i2c_driver max8925_driver = {
  	.driver	= {
  		.name	= "max8925",
  		.owner	= THIS_MODULE,
ba74e80eb   Kevin Liu   mfd: Add pm ops t...
205
  		.pm     = &max8925_pm_ops,
d50f8f339   Haojian Zhuang   mfd: Initial max8...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  	},
  	.probe		= max8925_probe,
  	.remove		= __devexit_p(max8925_remove),
  	.id_table	= max8925_id_table,
  };
  
  static int __init max8925_i2c_init(void)
  {
  	int ret;
  
  	ret = i2c_add_driver(&max8925_driver);
  	if (ret != 0)
  		pr_err("Failed to register MAX8925 I2C driver: %d
  ", ret);
  	return ret;
  }
  subsys_initcall(max8925_i2c_init);
  
  static void __exit max8925_i2c_exit(void)
  {
  	i2c_del_driver(&max8925_driver);
  }
  module_exit(max8925_i2c_exit);
  
  MODULE_DESCRIPTION("I2C Driver for Maxim 8925");
  MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
  MODULE_LICENSE("GPL");