Blame view

drivers/mfd/tps65910.c 5.01 KB
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
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
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
  /*
   * tps65910.c  --  TI TPS6591x
   *
   * Copyright 2010 Texas Instruments Inc.
   *
   * Author: Graeme Gregory <gg@slimlogic.co.uk>
   * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
   *
   *  This program is free software; you can redistribute it and/or modify it
   *  under  the terms of the GNU General  Public License as published by the
   *  Free Software Foundation;  either version 2 of the License, or (at your
   *  option) any later version.
   *
   */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/i2c.h>
  #include <linux/gpio.h>
  #include <linux/mfd/core.h>
  #include <linux/mfd/tps65910.h>
  
  static struct mfd_cell tps65910s[] = {
  	{
  		.name = "tps65910-pmic",
  	},
  	{
  		.name = "tps65910-rtc",
  	},
  	{
  		.name = "tps65910-power",
  	},
  };
  
  
  static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg,
  				  int bytes, void *dest)
  {
  	struct i2c_client *i2c = tps65910->i2c_client;
  	struct i2c_msg xfer[2];
  	int ret;
  
  	/* Write register */
  	xfer[0].addr = i2c->addr;
  	xfer[0].flags = 0;
  	xfer[0].len = 1;
  	xfer[0].buf = &reg;
  
  	/* Read data */
  	xfer[1].addr = i2c->addr;
  	xfer[1].flags = I2C_M_RD;
  	xfer[1].len = bytes;
  	xfer[1].buf = dest;
  
  	ret = i2c_transfer(i2c->adapter, xfer, 2);
  	if (ret == 2)
  		ret = 0;
  	else if (ret >= 0)
  		ret = -EIO;
  
  	return ret;
  }
  
  static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg,
  				   int bytes, void *src)
  {
  	struct i2c_client *i2c = tps65910->i2c_client;
  	/* we add 1 byte for device register */
  	u8 msg[TPS65910_MAX_REGISTER + 1];
  	int ret;
0514e9acd   Axel Lin   mfd: Fix off-by-o...
73
  	if (bytes > TPS65910_MAX_REGISTER)
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
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
  		return -EINVAL;
  
  	msg[0] = reg;
  	memcpy(&msg[1], src, bytes);
  
  	ret = i2c_master_send(i2c, msg, bytes + 1);
  	if (ret < 0)
  		return ret;
  	if (ret != bytes + 1)
  		return -EIO;
  	return 0;
  }
  
  int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
  {
  	u8 data;
  	int err;
  
  	mutex_lock(&tps65910->io_mutex);
  	err = tps65910_i2c_read(tps65910, reg, 1, &data);
  	if (err) {
  		dev_err(tps65910->dev, "read from reg %x failed
  ", reg);
  		goto out;
  	}
  
  	data |= mask;
  	err = tps65910_i2c_write(tps65910, reg, 1, &data);
  	if (err)
  		dev_err(tps65910->dev, "write to reg %x failed
  ", reg);
  
  out:
  	mutex_unlock(&tps65910->io_mutex);
  	return err;
  }
  EXPORT_SYMBOL_GPL(tps65910_set_bits);
  
  int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
  {
  	u8 data;
  	int err;
  
  	mutex_lock(&tps65910->io_mutex);
  	err = tps65910_i2c_read(tps65910, reg, 1, &data);
  	if (err) {
  		dev_err(tps65910->dev, "read from reg %x failed
  ", reg);
  		goto out;
  	}
8f6a459a9   Marcus Folkesson   mfd: Handle tps65...
124
  	data &= ~mask;
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  	err = tps65910_i2c_write(tps65910, reg, 1, &data);
  	if (err)
  		dev_err(tps65910->dev, "write to reg %x failed
  ", reg);
  
  out:
  	mutex_unlock(&tps65910->io_mutex);
  	return err;
  }
  EXPORT_SYMBOL_GPL(tps65910_clear_bits);
  
  static int tps65910_i2c_probe(struct i2c_client *i2c,
  			    const struct i2c_device_id *id)
  {
  	struct tps65910 *tps65910;
2537df722   Graeme Gregory   TPS65910: GPIO: A...
140
  	struct tps65910_board *pmic_plat_data;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
141
  	struct tps65910_platform_data *init_data;
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
142
  	int ret = 0;
2537df722   Graeme Gregory   TPS65910: GPIO: A...
143
144
145
  	pmic_plat_data = dev_get_platdata(&i2c->dev);
  	if (!pmic_plat_data)
  		return -EINVAL;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
146
147
148
  	init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL);
  	if (init_data == NULL)
  		return -ENOMEM;
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
149
  	tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
dc7e412d4   Jesper Juhl   mfd: Don't leak i...
150
151
  	if (tps65910 == NULL) {
  		kfree(init_data);
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
152
  		return -ENOMEM;
dc7e412d4   Jesper Juhl   mfd: Don't leak i...
153
  	}
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
154
155
156
157
  
  	i2c_set_clientdata(i2c, tps65910);
  	tps65910->dev = &i2c->dev;
  	tps65910->i2c_client = i2c;
795570561   Jorge Eduardo Candelaria   MFD: TPS65910: Ad...
158
  	tps65910->id = id->driver_data;
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
159
160
161
162
163
164
165
166
167
  	tps65910->read = tps65910_i2c_read;
  	tps65910->write = tps65910_i2c_write;
  	mutex_init(&tps65910->io_mutex);
  
  	ret = mfd_add_devices(tps65910->dev, -1,
  			      tps65910s, ARRAY_SIZE(tps65910s),
  			      NULL, 0);
  	if (ret < 0)
  		goto err;
b1224cd11   Jesper Juhl   mfd: Avoid two as...
168
169
  	init_data->irq = pmic_plat_data->irq;
  	init_data->irq_base = pmic_plat_data->irq;
2537df722   Graeme Gregory   TPS65910: GPIO: A...
170
  	tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
1e351a95b   Afzal Mohammed   mfd: Make TPS6591...
171
  	tps65910_irq_init(tps65910, init_data->irq, init_data);
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
172

dc7e412d4   Jesper Juhl   mfd: Don't leak i...
173
  	kfree(init_data);
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
174
175
176
  	return ret;
  
  err:
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
177
  	kfree(tps65910);
dc7e412d4   Jesper Juhl   mfd: Don't leak i...
178
  	kfree(init_data);
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
179
180
181
182
183
184
  	return ret;
  }
  
  static int tps65910_i2c_remove(struct i2c_client *i2c)
  {
  	struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
ec2328c30   Mark Brown   mfd: Implement tp...
185
  	tps65910_irq_exit(tps65910);
1e351a95b   Afzal Mohammed   mfd: Make TPS6591...
186
  	mfd_remove_devices(tps65910->dev);
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
187
188
189
190
191
192
  	kfree(tps65910);
  
  	return 0;
  }
  
  static const struct i2c_device_id tps65910_i2c_id[] = {
795570561   Jorge Eduardo Candelaria   MFD: TPS65910: Ad...
193
194
         { "tps65910", TPS65910 },
         { "tps65911", TPS65911 },
27c6750ec   Graeme Gregory   MFD: TPS65910: Ad...
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
         { }
  };
  MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id);
  
  
  static struct i2c_driver tps65910_i2c_driver = {
  	.driver = {
  		   .name = "tps65910",
  		   .owner = THIS_MODULE,
  	},
  	.probe = tps65910_i2c_probe,
  	.remove = tps65910_i2c_remove,
  	.id_table = tps65910_i2c_id,
  };
  
  static int __init tps65910_i2c_init(void)
  {
  	return i2c_add_driver(&tps65910_i2c_driver);
  }
  /* init early so consumer devices can complete system boot */
  subsys_initcall(tps65910_i2c_init);
  
  static void __exit tps65910_i2c_exit(void)
  {
  	i2c_del_driver(&tps65910_i2c_driver);
  }
  module_exit(tps65910_i2c_exit);
  
  MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
  MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
  MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
  MODULE_LICENSE("GPL");