📄 i2c-adap-ibm_ocp.c
字号:
/* ------------------------------------------------------------------------- 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鰏ti M鋖kki <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/init.h>#include "i2c.h"#include "i2c-algo-ibm_ocp.h"#include <asm/irq.h>#include <asm/io.h>#include <asm/ocp.h>/* * 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;static spinlock_t irq_driver_lock = SPIN_LOCK_UNLOCKED;/* ----- 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);}//// 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) { spin_lock_irq(&irq_driver_lock); if (iic_pending == 0) { interruptible_sleep_on_timeout(&(iic_wait[priv_data->index]), timeout*HZ ); } else iic_pending = 0; spin_unlock_irq(&irq_driver_lock); } 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: 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)//static 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 = HZ; 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; init_waitqueue_head(&(iic_wait[i])); if (iic_hw_resrc_init(i) == 0) { if (i2c_ocp_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 __exit iic_ibmocp_exit(void){ int i; for(i=0; i<IIC_NUMS; i++) { i2c_ocp_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_LICENSE("GPL");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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -