linux-cvs-patches
[Top] [All Lists]

CVS Update@linux-mips.org: linux

To: linux-cvs-patches@linux-mips.org
Subject: CVS Update@linux-mips.org: linux
From: ppopov@linux-mips.org
Date: Tue, 18 Jan 2005 05:25:02 +0000
Reply-to: linux-mips@linux-mips.org
Sender: linux-cvs-patches-bounce@linux-mips.org
CVSROOT:        /home/cvs
Module name:    linux
Changes by:     ppopov@ftp.linux-mips.org       05/01/18 05:24:56

Modified files:
        drivers/i2c/busses: Kconfig Makefile 
        include/linux  : i2c-id.h 
Added files:
        drivers/i2c/busses: i2c-au1550.c i2c-au1550.h 

Log message:
        Patch adds the Au1550 i2c driver based on the previous patch that
        adds some necessary defines.

diff -urN linux/drivers/i2c/busses/i2c-au1550.c 
linux/drivers/i2c/busses/i2c-au1550.c
--- linux/drivers/i2c/busses/i2c-au1550.c       1970/01/01 00:00:00
+++ linux/drivers/i2c/busses/i2c-au1550.c       Tue Jan 18 05:24:56 2005        
1.1
@@ -0,0 +1,435 @@
+/*
+ * i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface
+ * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
+ *
+ * 2.6 port by Matt Porter <mporter@kernel.crashing.org>
+ *
+ * The documentation describes this as an SMBus controller, but it doesn't
+ * understand any of the SMBus protocol in hardware.  It's really an I2C
+ * controller that could emulate most of the SMBus in software.
+ *
+ * This is just a skeleton adapter to use with the Au1550 PSC
+ * algorithm.  It was developed for the Pb1550, but will work with
+ * any Au1550 board that has a similar PSC configuration.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-pb1x00/pb1550.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "i2c-au1550.h"
+
+static int
+wait_xfer_done(struct i2c_au1550_data *adap)
+{
+       u32     stat;
+       int     i;
+       volatile psc_smb_t      *sp;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       /* Wait for Tx FIFO Underflow.
+       */
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               stat = sp->psc_smbevnt;
+               au_sync();
+               if ((stat & PSC_SMBEVNT_TU) != 0) {
+                       /* Clear it.  */
+                       sp->psc_smbevnt = PSC_SMBEVNT_TU;
+                       au_sync();
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+wait_ack(struct i2c_au1550_data *adap)
+{
+       u32     stat;
+       volatile psc_smb_t      *sp;
+
+       if (wait_xfer_done(adap))
+               return -ETIMEDOUT;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       stat = sp->psc_smbevnt;
+       au_sync();
+
+       if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int
+wait_master_done(struct i2c_au1550_data *adap)
+{
+       u32     stat;
+       int     i;
+       volatile psc_smb_t      *sp;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       /* Wait for Master Done.
+       */
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               stat = sp->psc_smbevnt;
+               au_sync();
+               if ((stat & PSC_SMBEVNT_MD) != 0)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd)
+{
+       volatile psc_smb_t      *sp;
+       u32                     stat;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       /* Reset the FIFOs, clear events.
+       */
+       sp->psc_smbpcr = PSC_SMBPCR_DC;
+       sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
+       au_sync();
+       do {
+               stat = sp->psc_smbpcr;
+               au_sync();
+       } while ((stat & PSC_SMBPCR_DC) != 0);
+
+       /* Write out the i2c chip address and specify operation
+       */
+       addr <<= 1;
+       if (rd)
+               addr |= 1;
+
+       /* Put byte into fifo, start up master.
+       */
+       sp->psc_smbtxrx = addr;
+       au_sync();
+       sp->psc_smbpcr = PSC_SMBPCR_MS;
+       au_sync();
+       if (wait_ack(adap))
+               return -EIO;
+       return 0;
+}
+
+static u32
+wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
+{
+       int     j;
+       u32     data, stat;
+       volatile psc_smb_t      *sp;
+
+       if (wait_xfer_done(adap))
+               return -EIO;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       j =  adap->xfer_timeout * 100;
+       do {
+               j--;
+               if (j <= 0)
+                       return -EIO;
+
+               stat = sp->psc_smbstat;
+               au_sync();
+               if ((stat & PSC_SMBSTAT_RE) == 0)
+                       j = 0;
+               else
+                       udelay(1);
+       } while (j > 0);
+       data = sp->psc_smbtxrx;
+       au_sync();
+       *ret_data = data;
+
+       return 0;
+}
+
+static int
+i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
+                   unsigned int len)
+{
+       int     i;
+       u32     data;
+       volatile psc_smb_t      *sp;
+
+       if (len == 0)
+               return 0;
+
+       /* A read is performed by stuffing the transmit fifo with
+        * zero bytes for timing, waiting for bytes to appear in the
+        * receive fifo, then reading the bytes.
+        */
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       i = 0;
+       while (i < (len-1)) {
+               sp->psc_smbtxrx = 0;
+               au_sync();
+               if (wait_for_rx_byte(adap, &data))
+                       return -EIO;
+
+               buf[i] = data;
+               i++;
+       }
+
+       /* The last byte has to indicate transfer done.
+       */
+       sp->psc_smbtxrx = PSC_SMBTXRX_STP;
+       au_sync();
+       if (wait_master_done(adap))
+               return -EIO;
+
+       data = sp->psc_smbtxrx;
+       au_sync();
+       buf[i] = data;
+       return 0;
+}
+
+static int
+i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
+                    unsigned int len)
+{
+       int     i;
+       u32     data;
+       volatile psc_smb_t      *sp;
+
+       if (len == 0)
+               return 0;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       i = 0;
+       while (i < (len-1)) {
+               data = buf[i];
+               sp->psc_smbtxrx = data;
+               au_sync();
+               if (wait_ack(adap))
+                       return -EIO;
+               i++;
+       }
+
+       /* The last byte has to indicate transfer done.
+       */
+       data = buf[i];
+       data |= PSC_SMBTXRX_STP;
+       sp->psc_smbtxrx = data;
+       au_sync();
+       if (wait_master_done(adap))
+               return -EIO;
+       return 0;
+}
+
+static int
+au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+       struct i2c_au1550_data *adap = i2c_adap->algo_data;
+       struct i2c_msg *p;
+       int i, err = 0;
+
+       for (i = 0; !err && i < num; i++) {
+               p = &msgs[i];
+               err = do_address(adap, p->addr, p->flags & I2C_M_RD);
+               if (err || !p->len)
+                       continue;
+               if (p->flags & I2C_M_RD)
+                       err = i2c_read(adap, p->buf, p->len);
+               else
+                       err = i2c_write(adap, p->buf, p->len);
+       }
+
+       /* Return the number of messages processed, or the error code.
+       */
+       if (err == 0)
+               err = num;
+       return err;
+}
+
+static u32
+au1550_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm au1550_algo = {
+       .name           = "Au1550 algorithm",
+       .id             = I2C_ALGO_AU1550,
+       .master_xfer    = au1550_xfer,
+       .functionality  = au1550_func,
+};
+
+/* 
+ * registering functions to load algorithms at runtime 
+ * Prior to calling us, the 50MHz clock frequency and routing
+ * must have been set up for the PSC indicated by the adapter.
+ */
+int
+i2c_au1550_add_bus(struct i2c_adapter *i2c_adap)
+{
+       struct i2c_au1550_data *adap = i2c_adap->algo_data;
+       volatile psc_smb_t      *sp;
+       u32     stat;
+
+       i2c_adap->algo = &au1550_algo;
+
+       /* Now, set up the PSC for SMBus PIO mode.
+       */
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+       sp->psc_ctrl = PSC_CTRL_DISABLE;
+       au_sync();
+       sp->psc_sel = PSC_SEL_PS_SMBUSMODE;
+       sp->psc_smbcfg = 0;
+       au_sync();
+       sp->psc_ctrl = PSC_CTRL_ENABLE;
+       au_sync();
+       do {
+               stat = sp->psc_smbstat;
+               au_sync();
+       } while ((stat & PSC_SMBSTAT_SR) == 0);
+
+       sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 |
+                               PSC_SMBCFG_DD_DISABLE);
+
+       /* Divide by 8 to get a 6.25 MHz clock.  The later protocol
+        * timings are based on this clock.
+        */
+       sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
+       sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
+       au_sync();
+
+       /* Set the protocol timer values.  See Table 71 in the
+        * Au1550 Data Book for standard timing values.
+        */
+       sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
+               PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
+               PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
+               PSC_SMBTMR_SET_CH(15);
+       au_sync();
+
+       sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
+       do {
+               stat = sp->psc_smbstat;
+               au_sync();
+       } while ((stat & PSC_SMBSTAT_DR) == 0);
+
+       return i2c_add_adapter(i2c_adap);
+}
+
+
+int
+i2c_au1550_del_bus(struct i2c_adapter *adap)
+{
+       return i2c_del_adapter(adap);
+}
+
+static int
+pb1550_reg(struct i2c_client *client)
+{
+       return 0;
+}
+
+static int
+pb1550_unreg(struct i2c_client *client)
+{
+       return 0;
+}
+
+static struct i2c_au1550_data pb1550_i2c_info = {
+       SMBUS_PSC_BASE, 200, 200
+};
+
+static struct i2c_adapter pb1550_board_adapter = {
+       name:              "pb1550 adapter",
+       id:                I2C_HW_AU1550_PSC,
+       algo:              NULL,
+       algo_data:         &pb1550_i2c_info,
+       client_register:   pb1550_reg,
+       client_unregister: pb1550_unreg,
+};
+
+/* BIG hack to support the control interface on the Wolfson WM8731
+ * audio codec on the Pb1550 board.  We get an address and two data
+ * bytes to write, create an i2c message, and send it across the
+ * i2c transfer function.  We do this here because we have access to
+ * the i2c adapter structure.
+ */
+static struct i2c_msg wm_i2c_msg;  /* We don't want this stuff on the stack */
+static u8 i2cbuf[2];
+
+int
+pb1550_wm_codec_write(u8 addr, u8 reg, u8 val)
+{
+       wm_i2c_msg.addr = addr;
+       wm_i2c_msg.flags = 0;
+       wm_i2c_msg.buf = i2cbuf;
+       wm_i2c_msg.len = 2;

[%d lines skipped]
38diff -urN linux/drivers/i2c/busses/i2c-au1550.h 
linux/drivers/i2c/busses/i2c-au1550.h
--- linux/drivers/i2c/busses/i2c-au1550.h       1970/01/01 00:00:00
+++ linux/drivers/i2c/busses/i2c-au1550.h       Tue Jan 18 05:24:56 2005        
1.1
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
+ * 2.6 port by Matt Porter <mporter@kernel.crashing.org>
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef I2C_AU1550_H
+#define I2C_AU1550_H
+
+struct i2c_au1550_data {
+       u32     psc_base;
+       int     xfer_timeout;
+       int     ack_timeout;
+};
+
+int i2c_au1550_add_bus(struct i2c_adapter *);
+int i2c_au1550_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_AU1550_H */
diff -urN linux/drivers/i2c/busses/Kconfig linux/drivers/i2c/busses/Kconfig
--- linux/drivers/i2c/busses/Kconfig    2005/01/13 14:05:58     1.23
+++ linux/drivers/i2c/busses/Kconfig    2005/01/18 05:24:56     1.24
@@ -74,6 +74,16 @@
          This driver can also be built as a module.  If so, the module
          will be called i2c-amd8111.
 
+config I2C_AU1550
+       tristate "Au1550 SMBus interface"
+       depends on I2C && SOC_AU1550
+       help
+         If you say yes to this option, support will be included for the
+         Au1550 SMBus interface.
+
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-au1550.
+
 config I2C_ELEKTOR
        tristate "Elektor ISA card"
        depends on I2C && ISA && BROKEN_ON_SMP
diff -urN linux/drivers/i2c/busses/Makefile linux/drivers/i2c/busses/Makefile
--- linux/drivers/i2c/busses/Makefile   2004/11/15 11:49:25     1.16
+++ linux/drivers/i2c/busses/Makefile   2005/01/18 05:24:56     1.17
@@ -8,6 +8,7 @@
 obj-$(CONFIG_I2C_AMD756)       += i2c-amd756.o
 obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
 obj-$(CONFIG_I2C_AMD8111)      += i2c-amd8111.o
+obj-$(CONFIG_I2C_AU1550)       += i2c-au1550.o
 obj-$(CONFIG_I2C_ELEKTOR)      += i2c-elektor.o
 obj-$(CONFIG_I2C_HYDRA)                += i2c-hydra.o
 obj-$(CONFIG_I2C_I801)         += i2c-i801.o
diff -urN linux/include/linux/i2c-id.h linux/include/linux/i2c-id.h
--- linux/include/linux/i2c-id.h        2005/01/13 14:06:53     1.29
+++ linux/include/linux/i2c-id.h        2005/01/18 05:24:56     1.30
@@ -200,6 +200,7 @@
 
 #define I2C_ALGO_SIBYTE 0x150000       /* Broadcom SiByte SOCs         */
 #define I2C_ALGO_SGI   0x160000        /* SGI algorithm                */
+#define I2C_ALGO_AU1550        0x170000        /* Au1550 PSC algorithm         
*/
 
 #define I2C_ALGO_EXP   0x800000        /* experimental                 */
 
@@ -273,6 +274,9 @@
 /* --- XSCALE on-chip adapters                          */
 #define I2C_HW_IOP3XX 0x00
 
+/* --- Au1550 PSC adapters adapters                                    */
+#define I2C_HW_AU1550_PSC      0x00
+
 /* --- SMBus only adapters                                             */
 #define I2C_HW_SMBUS_PIIX4     0x00
 #define I2C_HW_SMBUS_ALI15X3   0x01

<Prev in Thread] Current Thread [Next in Thread>