/* ***************************************************************************************
 * This file is a copy of l3-algo-bit.c with the following differences:
 *
 * 2003-07-11   Fae Ghodrat     Modified algorithm to be used with the backpaq:
 *                              write to l3 through backpaq registers
 *************************************************************************************** */

/* ***************************************************************************************
 * L3 bus algorithm module.
 *
 *  Copyright (C) 2001 Russell King, All Rights Reserved.
 *
 * 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.
 *
 *  Note that L3 buses can share the same pins as I2C buses, so we must
 *  _not_ generate an I2C start condition.  An I2C start condition is
 *  defined as a high-to-low transition of the data line while the clock
 *  is high.  Therefore, we must only change the data line while the
 *  clock is low.
 *************************************************************************************** */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/l3/l3.h>
#include <linux/l3/algo-bit.h>
#include <asm/arch/backpaq.h>

static DECLARE_MUTEX(l3_lock);

static struct l3_adapter l3_backpaq_adapter = {
	owner:		THIS_MODULE,
	name:		"l3-backpaq",
	lock:		&l3_lock,
	// algo:           &l3_algo,
};

static void sendbytes(int addr, const char *buf, int len)
{
        unsigned long data = (unsigned long)*buf | (unsigned long)BACKPAQ_AUDIO_L3_DOUBLE;

	if (len == 2) {
		BACKPAQ_AUDIO_L3_ADDR = addr;
	        BACKPAQ_AUDIO_L3_DATA = data;	
		udelay(1);
	} else {
	        BACKPAQ_AUDIO_L3_ADDR = addr;
		BACKPAQ_AUDIO_L3_DATA = *buf;	
		udelay(1);
	}
}

static int l3_backpaq_xfer(struct l3_backpaq_adapter *l3_adap, struct l3_msg msgs[], int num)
{
	int i;

	for (i = 0; i < num; i++) {
		struct l3_msg *pmsg = &msgs[i];

		if (!(pmsg->flags & L3_M_NOADDR)) {
			BACKPAQ_AUDIO_L3_ADDR = pmsg->addr;
		}

		if (pmsg->flags & L3_M_RD) {
			return -1;
		} else {
		        sendbytes(pmsg->addr, pmsg->buf, pmsg->len);
		} 
	}

	return num;
}

static struct l3_algorithm l3_algo = {
	name:	"BACKPAQ L3 algorithm",
	xfer:	l3_backpaq_xfer,
};

int l3_backpaq_add_bus(struct l3_adapter *adap)
{
	adap->algo = &l3_algo;
	return l3_add_adapter(adap);
}

int l3_backpaq_del_bus(struct l3_backpaq_adapter *adap)
{
	return l3_del_adapter(adap);
}


static int __init l3_init(struct bit_data *bits)
{
	return l3_backpaq_add_bus(&l3_backpaq_adapter);
}

static void __exit l3_exit(void)
{
	l3_backpaq_del_bus(&l3_backpaq_adapter);
}

module_init(l3_init);
module_exit(l3_exit);
