lkml.org 
[lkml]   [2002]   [Sep]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[patch 3/9]Four new i2c drivers and __init/__exit cleanup to i2c
Hello Linus,
New I2C drivers that have been adjusted after Russell King comments of August.
o i2c-algo-8xx.c
o i2c-pport.c
o i2c-adap-ibm_ocp.c
o i2c-pcf-epp.c
o Add new drivers to Config.in and Makefile.
o Add new drivers to i2c-core for initialization.
o Remove EXPORT_NO_SYMBOLS statement from i2c-dev, i2c-elektor and i2c-frodo.
o Cleanup init_module and cleanup_module adding __init and __exit to most drivers.
o Adjust i2c-elektor with cli/sti replacement.
--
ac9410@attbi.com
--- /dev/null 1994-07-17 19:46:18.000000000 -0400
+++ linux-2.5.34/drivers/i2c/i2c-adap-ibm_ocp.c 2002-08-07 22:38:57.000000000 -0400
@@ -0,0 +1,397 @@
+/*
+ -------------------------------------------------------------------------
+ i2c-adap-ibm_ocp.c i2c-hw access for the IIC peripheral on the IBM PPC 405
+ -------------------------------------------------------------------------
+
+ Ian DaSilva, MontaVista Software, Inc.
+ idasilva@mvista.com or source@mvista.com
+
+ Copyright 2000 MontaVista Software Inc.
+
+ Changes made to support the IIC peripheral on the IBM PPC 405
+
+
+ ----------------------------------------------------------------------------
+ This file was highly leveraged from i2c-elektor.c, which was created
+ by Simon G. Vogl and Hans Berglund:
+
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ 1998-99 Hans Berglund
+
+ With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
+ Frodo Looijaard <frodol@dds.nl>
+
+
+ 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.
+ ----------------------------------------------------------------------------
+
+ History: 01/20/12 - Armin
+ akuster@mvista.com
+ ported up to 2.4.16+
+
+ Version 02/03/25 - Armin
+ converted to ocp format
+ removed commented out or #if 0 code
+
+ TODO: convert to ocp_register
+ add PM hooks
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+#include "i2c-algo-ibm_ocp.h"
+#include <linux/i2c-id.h>
+#include <asm/ocp.h>
+
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+/*
+ * This next section is configurable, and it is used to set the number
+ * of i2c controllers in the system. The default number of instances is 1,
+ * however, this should be changed to reflect your system's configuration.
+ */
+
+/*
+ * The STB03xxx, with a PPC405 core, has two i2c controllers.
+ */
+//(sizeof(IIC_ADDR)/sizeof(struct iic_regs))
+extern iic_t *IIC_ADDR[];
+static struct iic_ibm iic_ibmocp_adaps[IIC_NUMS][5];
+
+static struct i2c_algo_iic_data *iic_ibmocp_data[IIC_NUMS];
+static struct i2c_adapter *iic_ibmocp_ops[IIC_NUMS];
+
+static int i2c_debug=0;
+static wait_queue_head_t iic_wait[IIC_NUMS];
+static int iic_pending;
+
+/* ----- global defines ----------------------------------------------- */
+#define DEB(x) if (i2c_debug>=1) x
+#define DEB2(x) if (i2c_debug>=2) x
+#define DEB3(x) if (i2c_debug>=3) x
+#define DEBE(x) x /* error messages */
+
+/* ----- local functions ---------------------------------------------- */
+
+//
+// Description: Write a byte to IIC hardware
+//
+static void iic_ibmocp_setbyte(void *data, int ctl, int val)
+{
+ // writeb resolves to a write to the specified memory location
+ // plus a call to eieio. eieio ensures that all instructions
+ // preceding it are completed before any further stores are
+ // completed.
+ // Delays at this level (to protect writes) are not needed here.
+ writeb(val, ctl);
+}
+
+
+//
+// Description: Read a byte from IIC hardware
+//
+static int iic_ibmocp_getbyte(void *data, int ctl)
+{
+ int val;
+
+ val = readb(ctl);
+ return (val);
+}
+
+
+//
+// Description: Return our slave address. This is the address
+// put on the I2C bus when another master on the bus wants to address us
+// as a slave
+//
+static int iic_ibmocp_getown(void *data)
+{
+ return(((struct iic_ibm *)(data))->iic_own);
+}
+
+
+//
+// Description: Return the clock rate
+//
+static int iic_ibmocp_getclock(void *data)
+{
+ return(((struct iic_ibm *)(data))->iic_clock);
+}
+
+
+#if 0
+static void iic_ibmocp_sleep(unsigned long timeout)
+{
+ schedule_timeout( timeout * HZ);
+}
+#endif
+
+
+//
+// Description: Put this process to sleep. We will wake up when the
+// IIC controller interrupts.
+//
+static void iic_ibmocp_waitforpin(void *data) {
+
+ int timeout = 2;
+ struct iic_ibm *priv_data = data;
+
+ //
+ // If interrupts are enabled (which they are), then put the process to
+ // sleep. This process will be awakened by two events -- either the
+ // the IIC peripheral interrupts or the timeout expires.
+ //
+ if (priv_data->iic_irq > 0) {
+ local_irq_disable();
+ if (iic_pending == 0) {
+ interruptible_sleep_on_timeout(&(iic_wait[priv_data->index]), timeout*HZ );
+ } else
+ iic_pending = 0;
+ local_irq_enable();
+ } else {
+ //
+ // If interrupts are not enabled then delay for a reasonable amount
+ // of time and return. We expect that by time we return to the calling
+ // function that the IIC has finished our requested transaction and
+ // the status bit reflects this.
+ //
+ // udelay is probably not the best choice for this since it is
+ // the equivalent of a busy wait
+ //
+ udelay(100);
+ }
+ //printk("iic_ibmocp_waitforpin: exitting\n");
+}
+
+
+//
+// Description: The registered interrupt handler
+//
+static void iic_ibmocp_handler(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+ int ret;
+ struct iic_regs *iic;
+ struct iic_ibm *priv_data = dev_id;
+ iic = (struct iic_regs *) priv_data->iic_base;
+ iic_pending = 1;
+ DEB2(printk("iic_ibmocp_handler: in interrupt handler\n"));
+ // Read status register
+ ret = readb((int) &(iic->sts));
+ DEB2(printk("iic_ibmocp_handler: status = %x\n", ret));
+ // Clear status register. See IBM PPC 405 reference manual for details
+ writeb(0x0a, (int) &(iic->sts));
+ wake_up_interruptible(&(iic_wait[priv_data->index]));
+}
+
+
+//
+// Description: This function is very hardware dependent. First, we lock
+// the region of memory where out registers exist. Next, we request our
+// interrupt line and register its associated handler. Our IIC peripheral
+// uses interrupt number 2, as specified by the 405 reference manual.
+//
+static int iic_hw_resrc_init(int instance)
+{
+
+ DEB(printk("iic_hw_resrc_init: Physical Base address: 0x%x\n", (u32) IIC_ADDR[instance] ));
+ iic_ibmocp_adaps[instance]->iic_base = (u32)ioremap((unsigned long)IIC_ADDR[instance],PAGE_SIZE);
+
+ DEB(printk("iic_hw_resrc_init: ioremapped base address: 0x%x\n", iic_ibmocp_adaps[instance]->iic_base));
+
+ if (iic_ibmocp_adaps[instance]->iic_irq > 0) {
+
+ if (request_irq(iic_ibmocp_adaps[instance]->iic_irq, iic_ibmocp_handler,
+ 0, "IBM OCP IIC", iic_ibmocp_adaps[instance]) < 0) {
+ printk(KERN_ERR "iic_hw_resrc_init: Request irq%d failed\n",
+ iic_ibmocp_adaps[instance]->iic_irq);
+ iic_ibmocp_adaps[instance]->iic_irq = 0;
+ } else {
+ DEB3(printk("iic_hw_resrc_init: Enabled interrupt\n"));
+ }
+ }
+ return 0;
+}
+
+
+//
+// Description: Release irq and memory
+//
+static void iic_ibmocp_release(void)
+{
+ int i;
+
+ for(i=0; i<IIC_NUMS; i++) {
+ struct iic_ibm *priv_data = (struct iic_ibm *)iic_ibmocp_data[i]->data;
+ if (priv_data->iic_irq > 0) {
+ disable_irq(priv_data->iic_irq);
+ free_irq(priv_data->iic_irq, 0);
+ }
+ kfree(iic_ibmocp_data[i]);
+ kfree(iic_ibmocp_ops[i]);
+ }
+}
+
+
+//
+// Description: Does nothing
+//
+static int iic_ibmocp_reg(struct i2c_client *client)
+{
+ return 0;
+}
+
+
+//
+// Description: Does nothing
+//
+static int iic_ibmocp_unreg(struct i2c_client *client)
+{
+ return 0;
+}
+
+
+//
+// Description: If this compiled as a module, then increment the count
+//
+static void iic_ibmocp_inc_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+
+//
+// Description: If this is a module, then decrement the count
+//
+static void iic_ibmocp_dec_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
+//
+// Description: Called when the module is loaded. This function starts the
+// cascade of calls up through the heirarchy of i2c modules (i.e. up to the
+// algorithm layer and into to the core layer)
+//
+int __init iic_ibmocp_init(void)
+{
+ int i;
+
+ printk(KERN_INFO "iic_ibmocp_init: IBM on-chip iic adapter module\n");
+
+ for(i=0; i<IIC_NUMS; i++) {
+ iic_ibmocp_data[i] = kmalloc(sizeof(struct i2c_algo_iic_data),GFP_KERNEL);
+ if(iic_ibmocp_data[i] == NULL) {
+ return -ENOMEM;
+ }
+ memset(iic_ibmocp_data[i], 0, sizeof(struct i2c_algo_iic_data));
+
+ switch (i) {
+ case 0:
+ iic_ibmocp_adaps[i]->iic_irq = IIC_IRQ(0);
+ break;
+ case 1:
+ iic_ibmocp_adaps[i]->iic_irq = IIC_IRQ(1);
+ break;
+ }
+ iic_ibmocp_adaps[i]->iic_clock = IIC_CLOCK;
+ iic_ibmocp_adaps[i]->iic_own = IIC_OWN;
+ iic_ibmocp_adaps[i]->index = i;
+
+ DEB(printk("irq %x\n", iic_ibmocp_adaps[i]->iic_irq));
+ DEB(printk("clock %x\n", iic_ibmocp_adaps[i]->iic_clock));
+ DEB(printk("own %x\n", iic_ibmocp_adaps[i]->iic_own));
+ DEB(printk("index %x\n", iic_ibmocp_adaps[i]->index));
+
+
+ iic_ibmocp_data[i]->data = (struct iic_regs *)iic_ibmocp_adaps[i];
+ iic_ibmocp_data[i]->setiic = iic_ibmocp_setbyte;
+ iic_ibmocp_data[i]->getiic = iic_ibmocp_getbyte;
+ iic_ibmocp_data[i]->getown = iic_ibmocp_getown;
+ iic_ibmocp_data[i]->getclock = iic_ibmocp_getclock;
+ iic_ibmocp_data[i]->waitforpin = iic_ibmocp_waitforpin;
+ iic_ibmocp_data[i]->udelay = 80;
+ iic_ibmocp_data[i]->mdelay = 80;
+ iic_ibmocp_data[i]->timeout = 100;
+
+ iic_ibmocp_ops[i] = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+ if(iic_ibmocp_ops[i] == NULL) {
+ return -ENOMEM;
+ }
+ memset(iic_ibmocp_ops[i], 0, sizeof(struct i2c_adapter));
+ strcpy(iic_ibmocp_ops[i]->name, "IBM OCP IIC adapter");
+ iic_ibmocp_ops[i]->id = I2C_HW_OCP;
+ iic_ibmocp_ops[i]->algo = NULL;
+ iic_ibmocp_ops[i]->algo_data = iic_ibmocp_data[i];
+ iic_ibmocp_ops[i]->inc_use = iic_ibmocp_inc_use;
+ iic_ibmocp_ops[i]->dec_use = iic_ibmocp_dec_use;
+ iic_ibmocp_ops[i]->client_register = iic_ibmocp_reg;
+ iic_ibmocp_ops[i]->client_unregister = iic_ibmocp_unreg;
+
+
+ init_waitqueue_head(&(iic_wait[i]));
+ if (iic_hw_resrc_init(i) == 0) {
+ if (i2c_iic_add_bus(iic_ibmocp_ops[i]) < 0)
+ return -ENODEV;
+ } else {
+ return -ENODEV;
+ }
+ DEB(printk(KERN_INFO "iic_ibmocp_init: found device at %#x.\n\n", iic_ibmocp_adaps[i]->iic_base));
+ }
+ return 0;
+}
+
+
+static void iic_ibmocp_exit(void)
+{
+ int i;
+
+ for(i=0; i<IIC_NUMS; i++) {
+ i2c_iic_del_bus(iic_ibmocp_ops[i]);
+ }
+ iic_ibmocp_release();
+}
+
+//
+// If modules is NOT defined when this file is compiled, then the MODULE_*
+// macros will resolve to nothing
+//
+MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for PPC 405 IIC bus adapter");
+MODULE_PARM(base, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(clock, "i");
+MODULE_PARM(own, "i");
+MODULE_PARM(i2c_debug,"i");
+
+
+module_init(iic_ibmocp_init);
+module_exit(iic_ibmocp_exit);
\
 
 \ /
  Last update: 2005-03-22 13:28    [W:0.035 / U:4.988 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site