Linux Audio

Check our new training course

Loading...
  1/*
  2 * drivers/i2c/busses/i2c-s6000.c
  3 *
  4 * Description: Driver for S6000 Family I2C Interface
  5 * Copyright (c) 2008 emlix GmbH
  6 * Author:	Oskar Schirmer <oskar@scara.com>
  7 *
  8 * Partially based on i2c-bfin-twi.c driver by <sonic.zhang@analog.com>
  9 * Copyright (c) 2005-2007 Analog Devices, Inc.
 10 *
 11 * This program is free software; you can redistribute it and/or modify
 12 * it under the terms of the GNU General Public License as published by
 13 * the Free Software Foundation; either version 2 of the License, or
 14 * (at your option) any later version.
 15 *
 16 * This program is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19 * GNU General Public License for more details.
 20 *
 21 * You should have received a copy of the GNU General Public License
 22 * along with this program; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
 24 */
 25
 26#include <linux/clk.h>
 27#include <linux/err.h>
 28#include <linux/module.h>
 29#include <linux/kernel.h>
 30#include <linux/init.h>
 31#include <linux/delay.h>
 32#include <linux/i2c.h>
 33#include <linux/i2c/s6000.h>
 34#include <linux/timer.h>
 35#include <linux/spinlock.h>
 36#include <linux/completion.h>
 37#include <linux/interrupt.h>
 38#include <linux/platform_device.h>
 39#include <linux/io.h>
 40
 41#include "i2c-s6000.h"
 42
 43#define DRV_NAME "i2c-s6000"
 44
 45#define POLL_TIMEOUT	(2 * HZ)
 46
 47struct s6i2c_if {
 48	u8 __iomem		*reg; /* memory mapped registers */
 49	int			irq;
 50	spinlock_t		lock;
 51	struct i2c_msg		*msgs; /* messages currently handled */
 52	int			msgs_num; /* nb of msgs to do */
 53	int			msgs_push; /* nb of msgs read/written */
 54	int			msgs_done; /* nb of msgs finally handled */
 55	unsigned		push; /* nb of bytes read/written in msg */
 56	unsigned		done; /* nb of bytes finally handled */
 57	int			timeout_count; /* timeout retries left */
 58	struct timer_list	timeout_timer;
 59	struct i2c_adapter	adap;
 60	struct completion	complete;
 61	struct clk		*clk;
 62	struct resource		*res;
 63};
 64
 65static inline u16 i2c_rd16(struct s6i2c_if *iface, unsigned n)
 66{
 67	return readw(iface->reg + (n));
 68}
 69
 70static inline void i2c_wr16(struct s6i2c_if *iface, unsigned n, u16 v)
 71{
 72	writew(v, iface->reg + (n));
 73}
 74
 75static inline u32 i2c_rd32(struct s6i2c_if *iface, unsigned n)
 76{
 77	return readl(iface->reg + (n));
 78}
 79
 80static inline void i2c_wr32(struct s6i2c_if *iface, unsigned n, u32 v)
 81{
 82	writel(v, iface->reg + (n));
 83}
 84
 85static struct s6i2c_if s6i2c_if;
 86
 87static void s6i2c_handle_interrupt(struct s6i2c_if *iface)
 88{
 89	if (i2c_rd16(iface, S6_I2C_INTRSTAT) & (1 << S6_I2C_INTR_TXABRT)) {
 90		i2c_rd16(iface, S6_I2C_CLRTXABRT);
 91		i2c_wr16(iface, S6_I2C_INTRMASK, 0);
 92		complete(&iface->complete);
 93		return;
 94	}
 95	if (iface->msgs_done >= iface->msgs_num) {
 96		dev_err(&iface->adap.dev, "s6i2c: spurious I2C irq: %04x\n",
 97			i2c_rd16(iface, S6_I2C_INTRSTAT));
 98		i2c_wr16(iface, S6_I2C_INTRMASK, 0);
 99		return;
100	}
101	while ((iface->msgs_push < iface->msgs_num)
102	    && (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_TFNF))) {
103		struct i2c_msg *m = &iface->msgs[iface->msgs_push];
104		if (!(m->flags & I2C_M_RD))
105			i2c_wr16(iface, S6_I2C_DATACMD, m->buf[iface->push]);
106		else
107			i2c_wr16(iface, S6_I2C_DATACMD,
108				 1 << S6_I2C_DATACMD_READ);
109		if (++iface->push >= m->len) {
110			iface->push = 0;
111			iface->msgs_push += 1;
112		}
113	}
114	do {
115		struct i2c_msg *m = &iface->msgs[iface->msgs_done];
116		if (!(m->flags & I2C_M_RD)) {
117			if (iface->msgs_done < iface->msgs_push)
118				iface->msgs_done += 1;
119			else
120				break;
121		} else if (i2c_rd16(iface, S6_I2C_STATUS)
122				& (1 << S6_I2C_STATUS_RFNE)) {
123			m->buf[iface->done] = i2c_rd16(iface, S6_I2C_DATACMD);
124			if (++iface->done >= m->len) {
125				iface->done = 0;
126				iface->msgs_done += 1;
127			}
128		} else{
129			break;
130		}
131	} while (iface->msgs_done < iface->msgs_num);
132	if (iface->msgs_done >= iface->msgs_num) {
133		i2c_wr16(iface, S6_I2C_INTRMASK, 1 << S6_I2C_INTR_TXABRT);
134		complete(&iface->complete);
135	} else if (iface->msgs_push >= iface->msgs_num) {
136		i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) |
137						 (1 << S6_I2C_INTR_RXFULL));
138	} else {
139		i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) |
140						 (1 << S6_I2C_INTR_TXEMPTY) |
141						 (1 << S6_I2C_INTR_RXFULL));
142	}
143}
144
145static irqreturn_t s6i2c_interrupt_entry(int irq, void *dev_id)
146{
147	struct s6i2c_if *iface = dev_id;
148	if (!(i2c_rd16(iface, S6_I2C_STATUS) & ((1 << S6_I2C_INTR_RXUNDER)
149					      | (1 << S6_I2C_INTR_RXOVER)
150					      | (1 << S6_I2C_INTR_RXFULL)
151					      | (1 << S6_I2C_INTR_TXOVER)
152					      | (1 << S6_I2C_INTR_TXEMPTY)
153					      | (1 << S6_I2C_INTR_RDREQ)
154					      | (1 << S6_I2C_INTR_TXABRT)
155					      | (1 << S6_I2C_INTR_RXDONE)
156					      | (1 << S6_I2C_INTR_ACTIVITY)
157					      | (1 << S6_I2C_INTR_STOPDET)
158					      | (1 << S6_I2C_INTR_STARTDET)
159					      | (1 << S6_I2C_INTR_GENCALL))))
160		return IRQ_NONE;
161
162	spin_lock(&iface->lock);
163	del_timer(&iface->timeout_timer);
164	s6i2c_handle_interrupt(iface);
165	spin_unlock(&iface->lock);
166	return IRQ_HANDLED;
167}
168
169static void s6i2c_timeout(unsigned long data)
170{
171	struct s6i2c_if *iface = (struct s6i2c_if *)data;
172	unsigned long flags;
173
174	spin_lock_irqsave(&iface->lock, flags);
175	s6i2c_handle_interrupt(iface);
176	if (--iface->timeout_count > 0) {
177		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
178		add_timer(&iface->timeout_timer);
179	} else {
180		complete(&iface->complete);
181		i2c_wr16(iface, S6_I2C_INTRMASK, 0);
182	}
183	spin_unlock_irqrestore(&iface->lock, flags);
184}
185
186static int s6i2c_master_xfer(struct i2c_adapter *adap,
187				struct i2c_msg *msgs, int num)
188{
189	struct s6i2c_if *iface = adap->algo_data;
190	int i;
191	if (num == 0)
192		return 0;
193	if (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY))
194		yield();
195	i2c_wr16(iface, S6_I2C_INTRMASK, 0);
196	i2c_rd16(iface, S6_I2C_CLRINTR);
197	for (i = 0; i < num; i++) {
198		if (msgs[i].flags & I2C_M_TEN) {
199			dev_err(&adap->dev,
200				"s6i2c: 10 bits addr not supported\n");
201			return -EINVAL;
202		}
203		if (msgs[i].len == 0) {
204			dev_err(&adap->dev,
205				"s6i2c: zero length message not supported\n");
206			return -EINVAL;
207		}
208		if (msgs[i].addr != msgs[0].addr) {
209			dev_err(&adap->dev,
210				"s6i2c: multiple xfer cannot change target\n");
211			return -EINVAL;
212		}
213	}
214
215	iface->msgs = msgs;
216	iface->msgs_num = num;
217	iface->msgs_push = 0;
218	iface->msgs_done = 0;
219	iface->push = 0;
220	iface->done = 0;
221	iface->timeout_count = 10;
222	i2c_wr16(iface, S6_I2C_TAR, msgs[0].addr);
223	i2c_wr16(iface, S6_I2C_ENABLE, 1);
224	i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXEMPTY) |
225					 (1 << S6_I2C_INTR_TXABRT));
226
227	iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
228	add_timer(&iface->timeout_timer);
229	wait_for_completion(&iface->complete);
230	del_timer_sync(&iface->timeout_timer);
231	while (i2c_rd32(iface, S6_I2C_TXFLR) > 0)
232		schedule();
233	while (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY))
234		schedule();
235
236	i2c_wr16(iface, S6_I2C_INTRMASK, 0);
237	i2c_wr16(iface, S6_I2C_ENABLE, 0);
238	return iface->msgs_done;
239}
240
241static u32 s6i2c_functionality(struct i2c_adapter *adap)
242{
243	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
244}
245
246static struct i2c_algorithm s6i2c_algorithm = {
247	.master_xfer   = s6i2c_master_xfer,
248	.functionality = s6i2c_functionality,
249};
250
251static u16 __devinit nanoseconds_on_clk(struct s6i2c_if *iface, u32 ns)
252{
253	u32 dividend = ((clk_get_rate(iface->clk) / 1000) * ns) / 1000000;
254	if (dividend > 0xffff)
255		return 0xffff;
256	return dividend;
257}
258
259static int __devinit s6i2c_probe(struct platform_device *dev)
260{
261	struct s6i2c_if *iface = &s6i2c_if;
262	struct i2c_adapter *p_adap;
263	const char *clock;
264	int bus_num, rc;
265	spin_lock_init(&iface->lock);
266	init_completion(&iface->complete);
267	iface->irq = platform_get_irq(dev, 0);
268	if (iface->irq < 0) {
269		rc = iface->irq;
270		goto err_out;
271	}
272	iface->res = platform_get_resource(dev, IORESOURCE_MEM, 0);
273	if (!iface->res) {
274		rc = -ENXIO;
275		goto err_out;
276	}
277	iface->res = request_mem_region(iface->res->start,
278					resource_size(iface->res),
279					dev->dev.bus_id);
280	if (!iface->res) {
281		rc = -EBUSY;
282		goto err_out;
283	}
284	iface->reg = ioremap_nocache(iface->res->start,
285				     resource_size(iface->res));
286	if (!iface->reg) {
287		rc = -ENOMEM;
288		goto err_reg;
289	}
290
291	clock = 0;
292	bus_num = -1;
293	if (dev->dev.platform_data) {
294		struct s6_i2c_platform_data *pdata = dev->dev.platform_data;
295		bus_num = pdata->bus_num;
296		clock = pdata->clock;
297	}
298	iface->clk = clk_get(&dev->dev, clock);
299	if (IS_ERR(iface->clk)) {
300		rc = PTR_ERR(iface->clk);
301		goto err_map;
302	}
303	rc = clk_enable(iface->clk);
304	if (rc < 0)
305		goto err_clk_put;
306	init_timer(&iface->timeout_timer);
307	iface->timeout_timer.function = s6i2c_timeout;
308	iface->timeout_timer.data = (unsigned long)iface;
309
310	p_adap = &iface->adap;
311	strlcpy(p_adap->name, dev->name, sizeof(p_adap->name));
312	p_adap->algo = &s6i2c_algorithm;
313	p_adap->algo_data = iface;
314	p_adap->nr = bus_num;
315	p_adap->class = 0;
316	p_adap->dev.parent = &dev->dev;
317	i2c_wr16(iface, S6_I2C_INTRMASK, 0);
318	rc = request_irq(iface->irq, s6i2c_interrupt_entry,
319			 IRQF_SHARED, dev->name, iface);
320	if (rc) {
321		dev_err(&p_adap->dev, "s6i2c: can't get IRQ %d\n", iface->irq);
322		goto err_clk_dis;
323	}
324
325	i2c_wr16(iface, S6_I2C_ENABLE, 0);
326	udelay(1);
327	i2c_wr32(iface, S6_I2C_SRESET, 1 << S6_I2C_SRESET_IC_SRST);
328	i2c_wr16(iface, S6_I2C_CLRTXABRT, 1);
329	i2c_wr16(iface, S6_I2C_CON,
330			(1 << S6_I2C_CON_MASTER) |
331			(S6_I2C_CON_SPEED_NORMAL << S6_I2C_CON_SPEED) |
332			(0 << S6_I2C_CON_10BITSLAVE) |
333			(0 << S6_I2C_CON_10BITMASTER) |
334			(1 << S6_I2C_CON_RESTARTENA) |
335			(1 << S6_I2C_CON_SLAVEDISABLE));
336	i2c_wr16(iface, S6_I2C_SSHCNT, nanoseconds_on_clk(iface, 4000));
337	i2c_wr16(iface, S6_I2C_SSLCNT, nanoseconds_on_clk(iface, 4700));
338	i2c_wr16(iface, S6_I2C_FSHCNT, nanoseconds_on_clk(iface, 600));
339	i2c_wr16(iface, S6_I2C_FSLCNT, nanoseconds_on_clk(iface, 1300));
340	i2c_wr16(iface, S6_I2C_RXTL, 0);
341	i2c_wr16(iface, S6_I2C_TXTL, 0);
342
343	platform_set_drvdata(dev, iface);
344	rc = i2c_add_numbered_adapter(p_adap);
345	if (rc)
346		goto err_irq_free;
347	return 0;
348
349err_irq_free:
350	free_irq(iface->irq, iface);
351err_clk_dis:
352	clk_disable(iface->clk);
353err_clk_put:
354	clk_put(iface->clk);
355err_map:
356	iounmap(iface->reg);
357err_reg:
358	release_mem_region(iface->res->start,
359			   resource_size(iface->res));
360err_out:
361	return rc;
362}
363
364static int __devexit s6i2c_remove(struct platform_device *pdev)
365{
366	struct s6i2c_if *iface = platform_get_drvdata(pdev);
367	i2c_wr16(iface, S6_I2C_ENABLE, 0);
368	platform_set_drvdata(pdev, NULL);
369	i2c_del_adapter(&iface->adap);
370	free_irq(iface->irq, iface);
371	clk_disable(iface->clk);
372	clk_put(iface->clk);
373	iounmap(iface->reg);
374	release_mem_region(iface->res->start,
375			   resource_size(iface->res));
376	return 0;
377}
378
379static struct platform_driver s6i2c_driver = {
380	.probe		= s6i2c_probe,
381	.remove		= __devexit_p(s6i2c_remove),
382	.driver		= {
383		.name	= DRV_NAME,
384		.owner	= THIS_MODULE,
385	},
386};
387
388static int __init s6i2c_init(void)
389{
390	pr_info("I2C: S6000 I2C driver\n");
391	return platform_driver_register(&s6i2c_driver);
392}
393
394static void __exit s6i2c_exit(void)
395{
396	platform_driver_unregister(&s6i2c_driver);
397}
398
399MODULE_DESCRIPTION("I2C-Bus adapter routines for S6000 I2C");
400MODULE_LICENSE("GPL");
401MODULE_ALIAS("platform:" DRV_NAME);
402
403subsys_initcall(s6i2c_init);
404module_exit(s6i2c_exit);