Blame view

drivers/mfd/mc13xxx-spi.c 5.02 KB
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * Copyright 2009-2010 Pengutronix
   * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
   *
   * loosely based on an earlier driver that has
   * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
   *
   * 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/slab.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  #include <linux/interrupt.h>
  #include <linux/mfd/core.h>
  #include <linux/mfd/mc13xxx.h>
  #include <linux/of.h>
  #include <linux/of_device.h>
  #include <linux/of_gpio.h>
  #include <linux/err.h>
  #include <linux/spi/spi.h>
  
  #include "mc13xxx.h"
  
  static const struct spi_device_id mc13xxx_device_id[] = {
  	{
  		.name = "mc13783",
cd0f34b08   Uwe Kleine-König   mfd: mc13xxx: Cha...
30
  		.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13783,
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
31
32
  	}, {
  		.name = "mc13892",
cd0f34b08   Uwe Kleine-König   mfd: mc13xxx: Cha...
33
  		.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892,
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
34
  	}, {
0312e024d   Uwe Kleine-König   mfd: mc13xxx: Add...
35
36
  		.name = "mc34708",
  		.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708,
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
37
38
39
40
41
42
43
  	}, {
  		/* sentinel */
  	}
  };
  MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
  
  static const struct of_device_id mc13xxx_dt_ids[] = {
cd0f34b08   Uwe Kleine-König   mfd: mc13xxx: Cha...
44
45
  	{ .compatible = "fsl,mc13783", .data = &mc13xxx_variant_mc13783, },
  	{ .compatible = "fsl,mc13892", .data = &mc13xxx_variant_mc13892, },
0312e024d   Uwe Kleine-König   mfd: mc13xxx: Add...
46
  	{ .compatible = "fsl,mc34708", .data = &mc13xxx_variant_mc34708, },
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
47
48
49
  	{ /* sentinel */ }
  };
  MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
18dd21ab1   Krzysztof Kozlowski   mfd: mc13xxx: i2c...
50
  static const struct regmap_config mc13xxx_regmap_spi_config = {
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
51
52
53
  	.reg_bits = 7,
  	.pad_bits = 1,
  	.val_bits = 24,
77a5b3701   Philippe Rétornaz   mfd: Fix mc13xxx ...
54
  	.write_flag_mask = 0x80,
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
55
56
57
58
  
  	.max_register = MC13XXX_NUMREGS,
  
  	.cache_type = REGCACHE_NONE,
e4ecf6ea8   Philippe Rétornaz   mfd: mc13xxx work...
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
  	.use_single_rw = 1,
  };
  
  static int mc13xxx_spi_read(void *context, const void *reg, size_t reg_size,
  				void *val, size_t val_size)
  {
  	unsigned char w[4] = { *((unsigned char *) reg), 0, 0, 0};
  	unsigned char r[4];
  	unsigned char *p = val;
  	struct device *dev = context;
  	struct spi_device *spi = to_spi_device(dev);
  	struct spi_transfer t = {
  		.tx_buf = w,
  		.rx_buf = r,
  		.len = 4,
  	};
  
  	struct spi_message m;
  	int ret;
  
  	if (val_size != 3 || reg_size != 1)
  		return -ENOTSUPP;
  
  	spi_message_init(&m);
  	spi_message_add_tail(&t, &m);
  	ret = spi_sync(spi, &m);
  
  	memcpy(p, &r[1], 3);
  
  	return ret;
  }
  
  static int mc13xxx_spi_write(void *context, const void *data, size_t count)
  {
  	struct device *dev = context;
  	struct spi_device *spi = to_spi_device(dev);
fd792f8fb   Mark Brown   mfd: mc13xxx: Mov...
95
  	const char *reg = data;
e4ecf6ea8   Philippe Rétornaz   mfd: mc13xxx work...
96
97
98
  
  	if (count != 4)
  		return -ENOTSUPP;
fd792f8fb   Mark Brown   mfd: mc13xxx: Mov...
99
100
101
  	/* include errata fix for spi audio problems */
  	if (*reg == MC13783_AUDIO_CODEC || *reg == MC13783_AUDIO_DAC)
  		spi_write(spi, data, count);
e4ecf6ea8   Philippe Rétornaz   mfd: mc13xxx work...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  	return spi_write(spi, data, count);
  }
  
  /*
   * We cannot use regmap-spi generic bus implementation here.
   * The MC13783 chip will get corrupted if CS signal is deasserted
   * and on i.Mx31 SoC (the target SoC for MC13783 PMIC) the SPI controller
   * has the following errata (DSPhl22960):
   * "The CSPI negates SS when the FIFO becomes empty with
   * SSCTL= 0. Software cannot guarantee that the FIFO will not
   * drain because of higher priority interrupts and the
   * non-realtime characteristics of the operating system. As a
   * result, the SS will negate before all of the data has been
   * transferred to/from the peripheral."
   * We workaround this by accessing the SPI controller with a
   * single transfert.
   */
  
  static struct regmap_bus regmap_mc13xxx_bus = {
  	.write = mc13xxx_spi_write,
  	.read = mc13xxx_spi_read,
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
123
124
125
126
  };
  
  static int mc13xxx_spi_probe(struct spi_device *spi)
  {
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
127
  	struct mc13xxx *mc13xxx;
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
128
  	int ret;
e7c706b1e   Axel Lin   mfd: Use devm_* A...
129
  	mc13xxx = devm_kzalloc(&spi->dev, sizeof(*mc13xxx), GFP_KERNEL);
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
130
131
  	if (!mc13xxx)
  		return -ENOMEM;
db9ef449a   Alexander Shiyan   mfd: mc13xxx: Sim...
132
  	dev_set_drvdata(&spi->dev, mc13xxx);
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
133
  	spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
134

db9ef449a   Alexander Shiyan   mfd: mc13xxx: Sim...
135
  	mc13xxx->irq = spi->irq;
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
136

0cfe5c90c   Alexander Shiyan   mfd: mc13xxx: Lim...
137
  	spi->max_speed_hz = spi->max_speed_hz ? : 26000000;
6a5926e69   Alexander Shiyan   mfd: mc13xxx: Add...
138
139
140
  	ret = spi_setup(spi);
  	if (ret)
  		return ret;
0cfe5c90c   Alexander Shiyan   mfd: mc13xxx: Lim...
141

e03088972   Axel Lin   mfd: Fix mc13xxx-...
142
143
144
  	mc13xxx->regmap = devm_regmap_init(&spi->dev, &regmap_mc13xxx_bus,
  					   &spi->dev,
  					   &mc13xxx_regmap_spi_config);
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
145
146
  	if (IS_ERR(mc13xxx->regmap)) {
  		ret = PTR_ERR(mc13xxx->regmap);
db9ef449a   Alexander Shiyan   mfd: mc13xxx: Sim...
147
148
  		dev_err(&spi->dev, "Failed to initialize regmap: %d
  ", ret);
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
149
150
  		return ret;
  	}
cd0f34b08   Uwe Kleine-König   mfd: mc13xxx: Cha...
151
152
153
  	if (spi->dev.of_node) {
  		const struct of_device_id *of_id =
  			of_match_device(mc13xxx_dt_ids, &spi->dev);
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
154

cd0f34b08   Uwe Kleine-König   mfd: mc13xxx: Cha...
155
  		mc13xxx->variant = of_id->data;
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
156
  	} else {
cd0f34b08   Uwe Kleine-König   mfd: mc13xxx: Cha...
157
158
159
  		const struct spi_device_id *id_entry = spi_get_device_id(spi);
  
  		mc13xxx->variant = (void *)id_entry->driver_data;
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
160
  	}
db9ef449a   Alexander Shiyan   mfd: mc13xxx: Sim...
161
  	return mc13xxx_common_init(&spi->dev);
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
162
  }
4740f73fe   Bill Pemberton   mfd: remove use o...
163
  static int mc13xxx_spi_remove(struct spi_device *spi)
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
164
  {
db9ef449a   Alexander Shiyan   mfd: mc13xxx: Sim...
165
  	return mc13xxx_common_exit(&spi->dev);
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
166
167
168
169
170
171
  }
  
  static struct spi_driver mc13xxx_spi_driver = {
  	.id_table = mc13xxx_device_id,
  	.driver = {
  		.name = "mc13xxx",
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
172
173
174
  		.of_match_table = mc13xxx_dt_ids,
  	},
  	.probe = mc13xxx_spi_probe,
84449216b   Bill Pemberton   mfd: remove use o...
175
  	.remove = mc13xxx_spi_remove,
a0c7c1d48   Marc Reilly   mfd: Move the mc1...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  };
  
  static int __init mc13xxx_init(void)
  {
  	return spi_register_driver(&mc13xxx_spi_driver);
  }
  subsys_initcall(mc13xxx_init);
  
  static void __exit mc13xxx_exit(void)
  {
  	spi_unregister_driver(&mc13xxx_spi_driver);
  }
  module_exit(mc13xxx_exit);
  
  MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
  MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
  MODULE_LICENSE("GPL v2");