📄 i2c-mp200.c
字号:
break; } if (count < msg->len) { spin_lock(&i2c_lock); outw((inw(I2C_REG_IICC0) | I2C_BIT_WREL0), I2C_REG_IICC0); spin_unlock(&i2c_lock); } else { spin_lock(&i2c_lock); outw((inw(I2C_REG_IICC0) & ~I2C_BIT_ACKE0) | I2C_BIT_WTIM0 | I2C_BIT_WREL0, I2C_REG_IICC0); spin_unlock(&i2c_lock); } } else if (status & I2C_BIT_TRC0) { if ((msg->flags & I2C_M_RD)) { printk(KERN_INFO "mp200_i2c_rxbytes(): %s r/w hardware fault.\n", adap->name); mp200_init(adap); return -EREMOTEIO; } if (!(status & I2C_BIT_ACKD0)) { printk(KERN_INFO "mp200_i2c_rxbytes(): %s r/w hardware fault.\n", adap->name); /* stop condition trigger */ spin_lock(&i2c_lock); outw((inw(I2C_REG_IICC0) | I2C_BIT_SPT0), I2C_REG_IICC0); spin_unlock(&i2c_lock); return -EREMOTEIO; } if (count < msg->len) { data = msg->buf[count++]; } else if (count == msg->len) { break; } else if (msg->len != 0) { /* Er, h/w and/or s/w error? */ printk(KERN_INFO "mp200_i2c_rxbytes(): %s xmt count mismatch.\n", adap->name); break; }#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_rxbytes(): write: data: %04x\n", data);#endif /* write data */ outw(data, I2C_REG_IIC0); } else {#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_rxbytes(): con: %04x, stat: %04x\n", inw(I2C_REG_IICC0), inw(I2C_REG_IICSE0));#endif } if (mp200_i2c_wait_for_pin(adap, &status)) { /* Timeout */ mp200_init(adap); return -EREMOTEIO; } } while (count <= msg->len);#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_rxbytes(): count: %d, len: %d, stop %d\n", count, msg->len, stop);#endif /* Create stop condition */ if (count >= msg->len && stop) { /* stop condition trigger */ spin_lock(&i2c_lock); outw((inw(I2C_REG_IICC0) | I2C_BIT_SPT0), I2C_REG_IICC0); spin_unlock(&i2c_lock);#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_rxbytes(): stop:: con: %04x, stat: %04x\n", inw(I2C_REG_IICC0), inw(I2C_REG_IICSE0));#endif } if (recv_len) { return -EINVAL; } /* Fixup return status for the silly 0 length probe case */ return (count > 0) ? (msg->len ? count : 0) : count;}/* Simulate a SMBus command using the i2c protocol No checking of parameters is done! */static s32mp200_i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data){ /* So we need to generate a series of msgs. In the case of writing, we need to use only one message; when reading, we need two. We initialize most things with sane defaults, to keep the code below somewhat simpler. */ unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 2]; unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2]; int num = read_write == I2C_SMBUS_READ ? 2 : 1; struct i2c_msg msg[2] = { {addr, flags, 1, msgbuf0}, {addr, flags | I2C_M_RD, 0, msgbuf1} }; int i, len; msgbuf0[0] = command; switch (size) { case I2C_SMBUS_QUICK: msg[0].len = 0; /* Special case: The read/write field is used as data */ msg[0].flags = flags | (read_write == I2C_SMBUS_READ) ? I2C_M_RD : 0; num = 1; break; case I2C_SMBUS_BYTE: if (read_write == I2C_SMBUS_READ) { /* Special case: only a read! */ msg[0].flags = I2C_M_RD | flags; num = 1; } break; case I2C_SMBUS_BYTE_DATA: if (read_write == I2C_SMBUS_READ) { msg[1].len = 1; } else { msg[0].len = 2; msgbuf0[1] = data->byte; } break; case I2C_SMBUS_WORD_DATA: if (read_write == I2C_SMBUS_READ) { msg[1].len = 2; } else { msg[0].len = 3; msgbuf0[1] = data->word & 0xff; msgbuf0[2] = (data->word >> 8) & 0xff; } break; case I2C_SMBUS_PROC_CALL: num = 2; /* Special case */ read_write = I2C_SMBUS_READ; msg[0].len = 3; msg[1].len = 2; msgbuf0[1] = data->word & 0xff; msgbuf0[2] = (data->word >> 8) & 0xff; break; case I2C_SMBUS_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { /* I2C_FUNC_SMBUS_EMUL doesn't include I2C_FUNC_SMBUS_READ_BLOCK_DATA */ if (!i2c_check_functionality (adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { printk(KERN_INFO "mp200_i2c_smbus_xfer_emulated(): " "Block read not supported under I2C emulation!\n"); return -1; } /* set send message */ msg[0].len = 1; /* set recv message */ msg[1].flags |= I2C_M_RECV_LEN; msg[1].len = I2C_SMBUS_BLOCK_MAX + 1; } else { msg[0].len = data->block[0] + 2; if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { printk(KERN_INFO "mp200_i2c_smbus_xfer_emulated(): " "smbus_access called with invalid block write size (%d)\n", data->block[0]); return -1; } for (i = 1; i <= msg[0].len; i++) { msgbuf0[i] = data->block[i - 1]; } } break; case I2C_SMBUS_I2C_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX; } else { msg[0].len = data->block[0] + 1; if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 1) { printk(KERN_INFO "mp200_i2c_smbus_xfer_emulated(): " "i2c_smbus_xfer_emulated called with invalid block " "write size (%d)\n", data->block[0]); return -1; } for (i = 1; i <= data->block[0]; i++) { msgbuf0[i] = data->block[i]; } } break; default: printk(KERN_INFO "mp200_i2c_smbus_xfer_emulated(): " "smbus_access called with invalid size (%d)\n", size); return -1; } /* i2c_transfer() uses I2C_LOCK(adap), * so need to call mp200_i2c_xfer() directly. */ if (mp200_i2c_xfer(adapter, msg, num) < 0) { return -1; } if (read_write == I2C_SMBUS_READ) { switch (size) { case I2C_SMBUS_BYTE: data->byte = msgbuf0[0]; break; case I2C_SMBUS_BYTE_DATA: data->byte = msgbuf1[0]; break; case I2C_SMBUS_WORD_DATA: case I2C_SMBUS_PROC_CALL: data->word = msgbuf1[0] | (msgbuf1[1] << 8); break; case I2C_SMBUS_I2C_BLOCK_DATA: /* fixed at 32 for now */ data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX; for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++) { data->block[i + 1] = msgbuf1[i]; } break; case I2C_SMBUS_BLOCK_DATA: len = msgbuf1[0] + 1; for (i = 0; i < len; i++) { data->block[i] = msgbuf1[i]; } break; } } return 0;}static intmp200_i2c_control(struct i2c_adapter *adap, unsigned int cmd, unsigned long arg){ i2c_ctrl_t ctrl; memset(&ctrl, 0, sizeof(ctrl)); switch (cmd) { case I2C_SET_CTRL: if (copy_from_user (&ctrl, (i2c_ctrl_t *) arg, sizeof(i2c_ctrl_t))) { return -EFAULT; } /* mode */ switch (ctrl.smc) { case I2C_SMC_NORMAL_SPEED: case I2C_SMC_HIGH_SPEED: break; default: return -EINVAL; } /* digital filter */ switch (ctrl.dfc) { case I2C_DFC_OFF: break; case I2C_DFC_ON: if (ctrl.smc != I2C_SMC_HIGH_SPEED) { return -EINVAL; } break; default: return -EINVAL; } /* clock speed */ switch (ctrl.clo) { case I2C_CLO_1: case I2C_CLO_2: case I2C_CLO_3: case I2C_CLO_4: break; default: return -EINVAL; } /* clock extened */ switch (ctrl.clx) { case I2C_CLX_NORMAL: break; case I2C_CLX_HIGH: /* It is used only high-speed mode and cannot select I2C_CLO_4 */ if (ctrl.smc != I2C_SMC_HIGH_SPEED) { return -EINVAL; } if (ctrl.clo == I2C_CLO_4) { return -EINVAL; } break; default: return -EINVAL; } if (mp200_i2c_wait_for_bb(adap)) { /* Timeout */ return -EFAULT; } *(i2c_ctrl_t *) adap->algo_data = ctrl; mp200_init(adap); break; case I2C_GET_CTRL: return copy_to_user((void *)arg, adap->algo_data, sizeof(i2c_ctrl_t)); default: return -EINVAL; }; return 0;}static u32 mp200_i2c_func(struct i2c_adapter *adap){ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA;}/* -----exported algorithm data: ------------------------------------- */static struct i2c_algorithm mp200_i2c_algo = { .name = "mp200 I2C algorithm", .id = I2C_ALGO_EXP, .master_xfer = mp200_i2c_xfer, .smbus_xfer = mp200_i2c_smbus_xfer_emulated, .slave_send = NULL, .slave_recv = NULL, .algo_control = mp200_i2c_control, .functionality = mp200_i2c_func,};/* * registering functions to load algorithms at runtime */int mp200_i2c_add_bus(struct i2c_adapter *adap){#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_add_bus(): hw routines for %s registered.\n", adap->name);#endif /* register new adapter to i2c module... */ adap->id |= mp200_i2c_algo.id; adap->algo = &mp200_i2c_algo; adap->timeout = 100; adap->retries = 3; i2c_add_adapter(adap); mp200_init(adap); if (i2c_scan) { if (mp200_i2c_scan_bus(adap) < 0) { return -ENODEV; } } return 0;}int mp200_i2c_del_bus(struct i2c_adapter *adap){ int res; if ((res = i2c_del_adapter(adap)) < 0) { return res; }#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_del_bus(): adapter unregistered: %s\n", adap->name);#endif return 0;}/* ----- local functions ---------------------------------------------- *//* */static irqreturn_tmp200_i2c_handler(int this_irq, void *dev_id, struct pt_regs *regs){ spin_lock(&i2c_irq_lock); i2c_pending = 1; spin_unlock(&i2c_irq_lock);#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_handler(): in interrupt handler\n");#endif wake_up_interruptible(&i2c_wait); return IRQ_HANDLED;}/* Lock the region of memory where I/O registers exist. Request our * interrupt line and register its associated handler. */static int mp200_i2c_hw_resrc_init(void){#ifdef I2C_DEBUG printk(KERN_INFO "mp200_i2c_hw_resrc_init(): resrc Initializing.\n");#endif if (irq) { if (request_irq(irq, mp200_i2c_handler, 0, "mp200_i2c", 0) < 0) { return -ENODEV; } } return 0;}static void mp200_i2c_release(void){ spin_lock(&i2c_lock); outw(0, I2C_REG_IICC0); spin_unlock(&i2c_lock); if (irq) { free_irq(irq, 0); }}/* ------------------------------------------------------------------------ * Encapsulate the above functions in the correct operations structure. * This is only done when more than one hardware adapter is supported. */static i2c_ctrl_t mp200_i2c_ctrl = { .smc = I2C_SMC_NORMAL_SPEED, /* mode */ .dfc = I2C_DFC_OFF, /* digital filter */ .clo = I2C_CLO_1, /* clock speed */ .clx = I2C_CLX_NORMAL, /* clock extended */};static struct i2c_adapter mp200_i2c_adap = { .owner = THIS_MODULE, .name = "mp200 I2C adapter", .id = I2C_ALGO_EXP, /* REVISIT: register for id */ .algo = &mp200_i2c_algo, .algo_data = &mp200_i2c_ctrl, .client_register = NULL, .client_unregister = NULL,};static int mp200_i2c_suspend(struct device *dev, u32 state, u32 level){ switch (level) { case SUSPEND_DISABLE: mp200_i2c_wait_for_bb(&mp200_i2c_adap); break; default: break; } return 0;}static int mp200_i2c_resume(struct device *dev, u32 level){ switch (level) { case RESUME_ENABLE: mp200_init(&mp200_i2c_adap); break; default: break; } return 0;}/* 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 mp200_i2c_probe(struct device *dev){ printk(KERN_INFO "Starting mp200_i2c.\n"); /* Check lower and upper I2C SCL bounds based upon * 12MHz I2C module clock source. */ if ((clock < 25000) || (clock > 400000)) { clock = DEFAULT_CLOCK; } init_waitqueue_head(&i2c_wait); if (mp200_i2c_hw_resrc_init() < 0) { return -ENODEV; } if (mp200_i2c_add_bus(&mp200_i2c_adap) < 0) { return -ENODEV; } return 0;}static int mp200_i2c_remove(struct device *dev){ mp200_i2c_del_bus(&mp200_i2c_adap); mp200_i2c_release(); return 0;}static struct device_driver mp200_i2c_driver = { .name = "i2c", .bus = &platform_bus_type, .probe = mp200_i2c_probe, .remove = mp200_i2c_remove, .suspend = mp200_i2c_suspend, .resume = mp200_i2c_resume,};static int __init mp200_i2c_init(void){ return driver_register(&mp200_i2c_driver);}static void __exit mp200_i2c_exit(void){ driver_unregister(&mp200_i2c_driver);}module_param(clock, int, 0);module_param(irq, int, 0);module_param(i2c_scan, int, 0);MODULE_PARM_DESC(i2c_scan, "Scan for active I2C clients on the bus");MODULE_LICENSE("GPL");module_init(mp200_i2c_init);module_exit(mp200_i2c_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -