/* * i2c.c */ #ifndef MODULE #define MODULE #endif #ifndef __KERNEL__ #define __KERNEL__ #endif #include #include #include #include #include #include #include #include #ifdef CONFIG_I2C #include #endif #include "hrt.h" #include "i2c.h" #include "saa7110.h" #ifdef CONFIG_I2C static int hrt_i2c_client_register(struct i2c_client *client); static int hrt_i2c_algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg); static u32 hrt_i2c_bit_func(struct i2c_adapter *adap); static int hrt_i2c_bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num); struct i2c_adapter hrt_i2c_adapter_template = { name: "hrt", id: HRT_I2C_HW_B, client_register: hrt_i2c_client_register }; struct i2c_client hrt_i2c_client_template = { name: "hrt internal", id: -1 }; struct i2c_algorithm hrt_i2c_algorithm = { name: "HRT-specific I2C algorithm", id: I2C_ALGO_EXP, master_xfer: hrt_i2c_bit_xfer, smbus_xfer: NULL, slave_send: NULL, slave_recv: NULL, algo_control: hrt_i2c_algo_control, functionality: hrt_i2c_bit_func }; #endif static int hrt_i2c_send_byte(hrt_dev_t *, unsigned long addr, unsigned char data); static int hrt_i2c_smbus_send_byte(hrt_dev_t *hrtdev, unsigned char byte); static void hrt_i2c_start(hrt_dev_t *, unsigned long addr); static void hrt_i2c_stop(hrt_dev_t *, unsigned long addr); static inline void hrt_sda(hrt_dev_t *hrt, unsigned long addr, int high) { if (high) hrt->i2c_port |= HRT_I2C_SDA; else hrt->i2c_port &= ~HRT_I2C_SDA; writeb(hrt->i2c_port, I2C_CONTROL(addr)); wmb(); } static inline void hrt_scl(hrt_dev_t *hrt, unsigned long addr, int high) { if (high) hrt->i2c_port |= HRT_I2C_SCL; else hrt->i2c_port &= ~HRT_I2C_SCL; writeb(hrt->i2c_port, I2C_CONTROL(addr)); wmb(); } static inline void hrt_sda_scl(hrt_dev_t *hrt, unsigned long addr, int sda_high, int scl_high) { if (sda_high) hrt->i2c_port |= HRT_I2C_SDA; else hrt->i2c_port &= ~HRT_I2C_SDA; if (scl_high) hrt->i2c_port |= HRT_I2C_SCL; else hrt->i2c_port &= ~HRT_I2C_SCL; writeb(hrt->i2c_port, I2C_CONTROL(addr)); wmb(); } /* quick read functions */ static inline int hrt_sda_read(hrt_dev_t *hrt, unsigned long addr) { char c = readb(I2C_CONTROL(addr)); return (c & HRT_I2C_SDA); } static inline int hrt_scl_read(hrt_dev_t *hrt, unsigned long addr) { char c = readb(I2C_CONTROL(addr)); return (c & HRT_I2C_SCL); } static inline void hrt_i2c_start(hrt_dev_t *hrt, unsigned long addr) { hrt_sda_scl(hrt, addr, 0, 0); udelay(5); hrt_sda(hrt, addr, 1); udelay(5); hrt_scl(hrt, addr, 1); udelay(5); hrt_sda(hrt, addr, 0); udelay(5); hrt_scl(hrt, addr, 0); } static inline void hrt_i2c_stop(hrt_dev_t *hrt, unsigned long addr) { hrt_sda_scl(hrt, addr, 0, 0); udelay(5); hrt_scl(hrt, addr, 1); udelay(5); hrt_sda(hrt, addr, 1); udelay(5); hrt_scl(hrt, addr, 0); hrt_scl(hrt, addr, 1); } #ifdef CONFIG_I2C static int hrt_i2c_client_register(struct i2c_client *client) { hrt_printk("client_register called\n"); return 0; } static int hrt_i2c_algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg) { return 0; } static u32 hrt_i2c_bit_func(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL; } static int hrt_i2c_bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { int i; hrt_dev_t *hrtdev = (hrt_dev_t *)i2c_adap->algo_data; for (i = 0; i < msgs[0].len; i++) { if(hrt_i2c_send_byte(hrtdev, hrtdev->virt_addr, msgs[0].buf[i])) return -1; } return 0; } static int hrt_i2c_add_bus(struct i2c_adapter *adap) { adap->id |= hrt_i2c_algorithm.id; adap->algo = &hrt_i2c_algorithm; i2c_add_adapter(adap); return 0; } #endif void hrt_i2c_release(hrt_dev_t *hrtdev) { #ifdef CONFIG_I2C if (hrtdev->i2c_adap.id & hrt_i2c_algorithm.id) i2c_del_adapter(&hrtdev->i2c_adap); #endif } /* should be part of dev */ int hrt_i2c_init_registers(hrt_dev_t *hrtdev, const char *sequence) { unsigned long addr; int i, len, cur_reg; len = (int) (*sequence++); addr = hrtdev->virt_addr; hrt_i2c_start(hrtdev, addr); /* here we select the A/D Device on the I2C bus * by telling it to listen to the following bytes */ if (hrt_i2c_smbus_send_byte(hrtdev, HRT_AD_DEVICE_ID)) { hrt_printk("send_byte failed\n"); return -1; } /* start at register 0 and increment along the way */ if (hrt_i2c_smbus_send_byte(hrtdev, cur_reg = 0)) { hrt_printk("send_byte failed(2)\n"); return -1; } for(i = 0; i < len; i += 2, sequence += 2) { char reg = sequence[0]; char data = sequence[1]; if (reg > HRT_SAA7110_NUMREGS) { hrt_printk("register number %02X out of range!\n", reg); return -1; } if (reg != cur_reg) { /* we're going to an entirely different register */ hrt_i2c_stop(hrtdev, addr); hrt_i2c_start(hrtdev, addr); /* set the chip */ if (hrt_i2c_smbus_send_byte(hrtdev, HRT_AD_DEVICE_ID)) { hrt_printk("send_byte failed(3)\n"); return -1; } /* set the register */ if (hrt_i2c_smbus_send_byte(hrtdev, cur_reg = reg)) { hrt_printk("send_byte failed(4)\n"); return -1; } } if (hrt_i2c_smbus_send_byte(hrtdev, data)) { hrt_printk("send_byte failed(5)\n"); return -1; } hrtdev->saa7110_registers[cur_reg++] = data; } /* free the i2c bus */ hrt_i2c_stop(hrtdev, addr); return 0; } int hrt_i2c_init_device(hrt_dev_t *hrtdev) { int res = 0; hrt_printk("hrt_i2c_init_device entered (virt_addr = %08X, addr = %08X)\n", (unsigned) hrtdev->virt_addr, (unsigned) hrtdev->phys_addr); #ifdef CONFIG_I2C memcpy(&hrtdev->i2c_adap, &hrt_i2c_adapter_template, sizeof(struct i2c_adapter)); memcpy(&hrtdev->i2c_client, &hrt_i2c_client_template, sizeof(struct i2c_client)); memcpy(&hrtdev->i2c_algo, &hrt_i2c_algorithm, sizeof(struct i2c_algorithm)); sprintf(hrtdev->i2c_adap.name, "HRT #%d", hrtdev->num); hrtdev->i2c_client.adapter = &hrtdev->i2c_adap; #ifndef KERNEL_2_6 hrtdev->i2c_adap.data = hrtdev; #endif hrtdev->i2c_adap.algo_data = hrtdev; hrtdev->i2c_port = 0; res = hrt_i2c_add_bus(&hrtdev->i2c_adap); if (res) { hrt_printk("hrt_i2c_add_bus failed\n"); return res; } #endif res = hrt_i2c_init_registers(hrtdev, saa7110_default_init_regs); if (res) { hrt_printk("hrt_i2c_init_registers failed\n"); return res; } return res; } static int hrt_i2c_send_bit(hrt_dev_t *hrt, unsigned long addr, unsigned char bit) { unsigned long timeout; if (bit) hrt_sda(hrt, addr, 1); else hrt_sda(hrt, addr, 0); udelay(5); writeb(hrt->i2c_port | 0x04, I2C_CONTROL(addr)); wmb(); udelay(5); if (I2C_BUSY(addr)) { timeout = jiffies + HZ / 10; while (I2C_BUSY(addr)) { if (jiffies > timeout) { hrt_printk("i2c bus timeout\n"); return -1; } } } return 0; } /* send/recv algorithm implemented here */ static int hrt_i2c_send_byte(hrt_dev_t *hrt, unsigned long addr, unsigned char data) { char bitpos, bit; for(bitpos = 0; bitpos < 8; bitpos++) { bit = (data & 0x80) >> 7; data <<= 1; if(hrt_i2c_send_bit(hrt, addr, bit)) goto failure; } udelay(5); hrt_sda_scl(hrt, addr, 1, 0); hrt_scl(hrt, addr, 1); /* leave clock high */ udelay(10); if (hrt_sda_read(hrt, addr)) { hrt_printk("no i2c ack\n"); goto failure; } hrt_sda_scl(hrt, addr, 1, 0); return 0; failure: hrt_sda_scl(hrt, addr, 1, 0); hrt_i2c_stop(hrt, addr); return -1; } /* calls the kernel i2c code, which in turn calls the algorithm above */ static int hrt_i2c_smbus_send_byte(hrt_dev_t *hrtdev, unsigned char byte) { #ifdef CONFIG_I2C /* let the kernel work its magik */ return i2c_smbus_write_byte(&hrtdev->i2c_client, byte); #else return hrt_i2c_send_byte(hrtdev, hrtdev->virt_addr, byte); #endif }