⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c-ibm_iic.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 2 页
字号:
				add_wait_queue(&dev->wq, &wait);		set_current_state(TASK_INTERRUPTIBLE);		if (in_8(&iic->sts) & STS_PT)			schedule_timeout(dev->adap.timeout * HZ);		set_current_state(TASK_RUNNING);		remove_wait_queue(&dev->wq, &wait);				if (unlikely(signal_pending(current))){			DBG("%d: wait interrupted\n", dev->idx);			ret = -ERESTARTSYS;		} else if (unlikely(in_8(&iic->sts) & STS_PT)){			DBG("%d: wait timeout\n", dev->idx);			ret = -ETIMEDOUT;		}	}	else {		/* Polling mode */		unsigned long x = jiffies + dev->adap.timeout * HZ;				while (in_8(&iic->sts) & STS_PT){			if (unlikely(time_after(jiffies, x))){				DBG("%d: poll timeout\n", dev->idx);				ret = -ETIMEDOUT;				break;			}					if (unlikely(signal_pending(current))){				DBG("%d: poll interrupted\n", dev->idx);				ret = -ERESTARTSYS;				break;			}			schedule();		}		}		if (unlikely(ret < 0))		iic_abort_xfer(dev);	else		ret = iic_xfer_result(dev);		DBG2("%d: iic_wait_for_tc -> %d\n", dev->idx, ret);		return ret;}/* * Low level master transfer routine */static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, 			  int combined_xfer){	volatile struct iic_regs *iic = dev->vaddr;	char* buf = pm->buf;	int i, j, loops, ret = 0;	int len = pm->len;	u8 cntl = (in_8(&iic->cntl) & CNTL_AMD) | CNTL_PT;	if (pm->flags & I2C_M_RD)		cntl |= CNTL_RW;		loops = (len + 3) / 4;	for (i = 0; i < loops; ++i, len -= 4){		int count = len > 4 ? 4 : len;		u8 cmd = cntl | ((count - 1) << CNTL_TCT_SHIFT);				if (!(cntl & CNTL_RW))			for (j = 0; j < count; ++j)				out_8((volatile u8*)&iic->mdbuf, *buf++);				if (i < loops - 1)			cmd |= CNTL_CHT;		else if (combined_xfer)			cmd |= CNTL_RPST;				DBG2("%d: xfer_bytes, %d, CNTL = 0x%02x\n", dev->idx, count, cmd);				/* Start transfer */		out_8(&iic->cntl, cmd);				/* Wait for completion */		ret = iic_wait_for_tc(dev);		if (unlikely(ret < 0))			break;		else if (unlikely(ret != count)){			DBG("%d: xfer_bytes, requested %d, transfered %d\n", 				dev->idx, count, ret);						/* If it's not a last part of xfer, abort it */			if (combined_xfer || (i < loops - 1))    				iic_abort_xfer(dev);							ret = -EREMOTEIO;			break;						}				if (cntl & CNTL_RW)			for (j = 0; j < count; ++j)				*buf++ = in_8((volatile u8*)&iic->mdbuf);	}		return ret > 0 ? 0 : ret;}/* * Set target slave address for master transfer */static inline void iic_address(struct ibm_iic_private* dev, struct i2c_msg* msg){	volatile struct iic_regs *iic = dev->vaddr;	u16 addr = msg->addr;		DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx, 		addr, msg->flags & I2C_M_TEN ? 10 : 7);		if (msg->flags & I2C_M_TEN){	    out_8(&iic->cntl, CNTL_AMD);	    out_8(&iic->lmadr, addr);	    out_8(&iic->hmadr, 0xf0 | ((addr >> 7) & 0x06));	}	else {	    out_8(&iic->cntl, 0);	    out_8(&iic->lmadr, addr << 1);	}}static inline int iic_invalid_address(const struct i2c_msg* p){	return (p->addr > 0x3ff) || (!(p->flags & I2C_M_TEN) && (p->addr > 0x7f));}static inline int iic_address_neq(const struct i2c_msg* p1, 				  const struct i2c_msg* p2){	return (p1->addr != p2->addr) 		|| ((p1->flags & I2C_M_TEN) != (p2->flags & I2C_M_TEN));} /* * Generic master transfer entrypoint.  * Returns the number of processed messages or error (<0) */static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num){    	struct ibm_iic_private* dev = (struct ibm_iic_private*)(i2c_get_adapdata(adap));	volatile struct iic_regs *iic = dev->vaddr;	int i, ret = 0;		DBG2("%d: iic_xfer, %d msg(s)\n", dev->idx, num);		if (!num)		return 0;		/* Check the sanity of the passed messages.	 * Uhh, generic i2c layer is more suitable place for such code...	 */	if (unlikely(iic_invalid_address(&msgs[0]))){		DBG("%d: invalid address 0x%03x (%d-bit)\n", dev->idx, 			msgs[0].addr, msgs[0].flags & I2C_M_TEN ? 10 : 7);		return -EINVAL;	}			for (i = 0; i < num; ++i){		if (unlikely(msgs[i].len <= 0)){			if (num == 1 && !msgs[0].len){				/* Special case for I2C_SMBUS_QUICK emulation.				 * IBM IIC doesn't support 0-length transactions				 * so we have to emulate them using bit-banging.				 */				return iic_smbus_quick(dev, &msgs[0]);			}			DBG("%d: invalid len %d in msg[%d]\n", dev->idx, 				msgs[i].len, i);			return -EINVAL;		}		if (unlikely(iic_address_neq(&msgs[0], &msgs[i]))){			DBG("%d: invalid addr in msg[%d]\n", dev->idx, i);			return -EINVAL;		}	}		/* Check bus state */	if (unlikely((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE)){		DBG("%d: iic_xfer, bus is not free\n", dev->idx);				/* Usually it means something serious has happend.		 * We *cannot* have unfinished previous transfer		 * so it doesn't make any sense to try to stop it.		 * Probably we were not able to recover from the 		 * previous error.		 * The only *reasonable* thing I can think of here		 * is soft reset.  --ebs		 */		iic_dev_reset(dev);				if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){			DBG("%d: iic_xfer, bus is still not free\n", dev->idx);			return -EREMOTEIO;		}	} 	else {		/* Flush master data buffer (just in case) */		out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB);	}		/* Load slave address */	iic_address(dev, &msgs[0]);		/* Do real transfer */    	for (i = 0; i < num && !ret; ++i)		ret = iic_xfer_bytes(dev, &msgs[i], i < num - 1);	return ret < 0 ? ret : num;}static u32 iic_func(struct i2c_adapter *adap){	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;}static struct i2c_algorithm iic_algo = {	.name 		= "IBM IIC algorithm",	.id   		= I2C_ALGO_OCP,	.master_xfer 	= iic_xfer,	.smbus_xfer	= NULL,	.slave_send	= NULL,	.slave_recv	= NULL,	.algo_control	= NULL,	.functionality	= iic_func};/* * Calculates IICx_CLCKDIV value for a specific OPB clock frequency */static inline u8 iic_clckdiv(unsigned int opb){	/* Compatibility kludge, should go away after all cards	 * are fixed to fill correct value for opbfreq.	 * Previous driver version used hardcoded divider value 4,	 * it corresponds to OPB frequency from the range (40, 50] MHz	 */	if (!opb){		printk(KERN_WARNING "ibm-iic: using compatibility value for OPB freq,"			" fix your board specific setup\n");		opb = 50000000;	}	/* Convert to MHz */	opb /= 1000000;		if (opb < 20 || opb > 150){		printk(KERN_CRIT "ibm-iic: invalid OPB clock frequency %u MHz\n",			opb);		opb = opb < 20 ? 20 : 150;	}	return (u8)((opb + 9) / 10 - 1);}/* * Register single IIC interface */static int __devinit iic_probe(struct ocp_device *ocp){	struct ibm_iic_private* dev;	struct i2c_adapter* adap;	struct ocp_func_iic_data* iic_data = ocp->def->additions;	int ret;		if (!iic_data)		printk(KERN_WARNING"ibm-iic%d: missing additional data!\n",			ocp->def->index);	if (!(dev = kmalloc(sizeof(*dev), GFP_KERNEL))){		printk(KERN_CRIT "ibm-iic%d: failed to allocate device data\n",			ocp->def->index);		return -ENOMEM;	}	memset(dev, 0, sizeof(*dev));	dev->idx = ocp->def->index;	ocp_set_drvdata(ocp, dev);		if (!(dev->vaddr = ioremap(ocp->def->paddr, sizeof(struct iic_regs)))){		printk(KERN_CRIT "ibm-iic%d: failed to ioremap device registers\n",			dev->idx);		ret = -ENXIO;		goto fail2;	}		init_waitqueue_head(&dev->wq);	dev->irq = iic_force_poll ? -1 : ocp->def->irq;	if (dev->irq >= 0){		/* Disable interrupts until we finish intialization,		   assumes level-sensitive IRQ setup...		 */		iic_interrupt_mode(dev, 0);		if (request_irq(dev->irq, iic_handler, 0, "IBM IIC", dev)){			printk(KERN_ERR "ibm-iic%d: request_irq %d failed\n", 				dev->idx, dev->irq);			/* Fallback to the polling mode */				dev->irq = -1;		}	}		if (dev->irq < 0)		printk(KERN_WARNING "ibm-iic%d: using polling mode\n", 			dev->idx);			/* Board specific settings */	dev->fast_mode = iic_force_fast ? 1 : (iic_data ? iic_data->fast_mode : 0);		/* clckdiv is the same for *all* IIC interfaces, 	 * but I'd rather make a copy than introduce another global. --ebs	 */	dev->clckdiv = iic_clckdiv(ocp_sys_info.opb_bus_freq);	DBG("%d: clckdiv = %d\n", dev->idx, dev->clckdiv);		/* Initialize IIC interface */	iic_dev_init(dev);		/* Register it with i2c layer */	adap = &dev->adap;	strcpy(adap->name, "IBM IIC");	i2c_set_adapdata(adap, dev);	adap->id = I2C_HW_OCP | iic_algo.id;	adap->algo = &iic_algo;	adap->client_register = NULL;	adap->client_unregister = NULL;	adap->timeout = 1;	adap->retries = 1;	if ((ret = i2c_add_adapter(adap)) != 0){		printk(KERN_CRIT "ibm-iic%d: failed to register i2c adapter\n",			dev->idx);		goto fail;	}		printk(KERN_INFO "ibm-iic%d: using %s mode\n", dev->idx,		dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)");	return 0;fail:		if (dev->irq >= 0){		iic_interrupt_mode(dev, 0);		free_irq(dev->irq, dev);	}		iounmap((void*)dev->vaddr);fail2:		ocp_set_drvdata(ocp, 0);	kfree(dev);		return ret;}/* * Cleanup initialized IIC interface */static void __devexit iic_remove(struct ocp_device *ocp){	struct ibm_iic_private* dev = (struct ibm_iic_private*)ocp_get_drvdata(ocp);	BUG_ON(dev == NULL);	if (i2c_del_adapter(&dev->adap)){		printk(KERN_CRIT "ibm-iic%d: failed to delete i2c adapter :(\n",			dev->idx);		/* That's *very* bad, just shutdown IRQ ... */		if (dev->irq >= 0){		    iic_interrupt_mode(dev, 0);			    free_irq(dev->irq, dev);		    dev->irq = -1;		}	} else {		if (dev->irq >= 0){		    iic_interrupt_mode(dev, 0);			    free_irq(dev->irq, dev);		}		iounmap((void*)dev->vaddr);		kfree(dev);	}}static struct ocp_device_id ibm_iic_ids[] __devinitdata = {	{ .vendor = OCP_VENDOR_IBM, .function = OCP_FUNC_IIC },	{ .vendor = OCP_VENDOR_INVALID }};MODULE_DEVICE_TABLE(ocp, ibm_iic_ids);static struct ocp_driver ibm_iic_driver ={	.name 		= "iic",	.id_table	= ibm_iic_ids,	.probe		= iic_probe,	.remove		= __devexit_p(iic_remove),#if defined(CONFIG_PM)	.suspend	= NULL,	.resume		= NULL,#endif};static int __init iic_init(void){	printk(KERN_INFO "IBM IIC driver v" DRIVER_VERSION "\n");	return ocp_register_driver(&ibm_iic_driver);}static void __exit iic_exit(void){	ocp_unregister_driver(&ibm_iic_driver);}module_init(iic_init);module_exit(iic_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -