| #include <config.h> |
| #include <init.h> |
| #include <common.h> |
| #include <malloc.h> |
| #include <i2c/i2c.h> |
| #include <asm/io.h> |
| #include <mach/gpio.h> |
| #include <mach/comcerto-2000.h> |
| #include <mach/i2c.h> |
| |
| #define DEFAULT_BUS_FREQ 100000 |
| |
| static bool is_initialized = false; |
| |
| /* Assuming that there is only one master on the bus (us) */ |
| |
| static void i2c_init (struct device_d *pdev) |
| { |
| unsigned int n, m, freq, margin, power; |
| unsigned int actualN = 0, actualM = 0; |
| unsigned int minMargin = 0xffffffff; |
| unsigned int control, status; |
| unsigned int tclk = CFG_TCLK; |
| unsigned int i2cFreq = DEFAULT_BUS_FREQ; |
| struct i2c_platform_data *pdata = pdev->platform_data; |
| if (pdata && pdata->bitrate) |
| i2cFreq = pdata->bitrate; |
| |
| dev_dbg(pdev, "i2c_init\n"); |
| |
| for (n = 0; n < 8; n++) { |
| for (m = 0; m < 16; m++) { |
| power = 1 << n; /* power = 2^(n) */ |
| freq = tclk / (10 * (m + 1) * power); |
| if (i2cFreq > freq) |
| margin = i2cFreq - freq; |
| else |
| margin = freq - i2cFreq; |
| if (margin < minMargin) { |
| minMargin = margin; |
| actualN = n; |
| actualM = m; |
| } |
| } |
| } |
| |
| dev_dbg(pdev, "setup i2c bus\n"); |
| |
| /* Setup bus */ |
| writel(0, pdev->map_base + I2C_SOFT_RESET); |
| |
| dev_dbg(pdev, "udelay...\n"); |
| |
| udelay (I2C_DELAY); |
| |
| dev_dbg(pdev, "set baudrate\n"); |
| |
| writel((actualM << 3) | actualN, pdev->map_base + I2C_STAT); |
| writel(I2C_AAK | I2C_ENAB, pdev->map_base + I2C_CNTR); |
| |
| udelay (I2C_DELAY * 10); |
| |
| dev_dbg(pdev, "read control, baudrate\n"); |
| |
| status = readl(pdev->map_base + I2C_STAT); |
| control = readl(pdev->map_base + I2C_CNTR); |
| |
| is_initialized = true; |
| } |
| |
| static uchar i2c_start (struct device_d *pdev) |
| { |
| unsigned int control, status; |
| int count = 0; |
| |
| /* Set the start bit */ |
| control = readl(pdev->map_base + I2C_CNTR); |
| control |= I2C_STA; /* generate the I2C_START_BIT */ |
| writel(control, pdev->map_base + I2C_CNTR); |
| |
| status = readl(pdev->map_base + I2C_STAT); |
| |
| count = 0; |
| while ((status & 0xff) != I2C_START_TRANSMIT) { |
| udelay (I2C_DELAY); |
| if (count > 20) { |
| writel(I2C_STP, pdev->map_base + I2C_CNTR); /*stop */ |
| dev_dbg(pdev, "i2c_start status timeout: 0x%x\n", status); |
| return (status); |
| } |
| status = readl(pdev->map_base + I2C_STAT); |
| dev_dbg(pdev, "i2c_start status 0x%x\n", status); |
| count++; |
| } |
| |
| return (0); |
| } |
| |
| static uchar i2c_select_device (struct device_d *pdev, uchar dev_addr, uchar read, int ten_bit) |
| { |
| unsigned int status, data, bits = 7; |
| int count = 0; |
| |
| /* Output slave address */ |
| if (ten_bit) { |
| bits = 10; |
| } |
| |
| data = (dev_addr << 1); |
| /* set the read bit */ |
| data |= read; |
| writel(data, pdev->map_base + I2C_DATA); |
| /* assert the address */ |
| writel(readl(pdev->map_base + I2C_CNTR) & ~I2C_IFLG, pdev->map_base + I2C_CNTR); |
| |
| udelay (I2C_DELAY); |
| |
| status = readl(pdev->map_base + I2C_STAT); |
| count = 0; |
| while (((status & 0xff) != I2C_ADDRESS_R_ACK) && ((status & 0xff) != I2C_ADDRESS_W_ACK)) { |
| udelay (I2C_DELAY); |
| if (count > 20) { |
| writel(I2C_STP, pdev->map_base + I2C_CNTR); /*stop */ |
| dev_dbg(pdev, "i2c_select_device timeout status 0x%x\n",status); |
| return (status); |
| } |
| status = readl(pdev->map_base + I2C_STAT); |
| dev_dbg(pdev, "i2c_select_device status 0x%x\n",status); |
| count++; |
| } |
| |
| if (bits == 10) { |
| dev_dbg(pdev, "10 bit I2C addressing not yet implemented\n"); |
| return (0xff); |
| } |
| |
| return (0); |
| } |
| |
| static uchar i2c_get_data (struct device_d *pdev, uchar *return_data, int len) |
| { |
| |
| unsigned int data, status = 0; |
| int count = 0; |
| uint32_t ack_expected = I2C_DATA_RECEIVE_ACK; |
| |
| while (len) { |
| count = 0; |
| |
| if (len > 1) { |
| /* more data to read, acknowledge the received byte */ |
| writel(I2C_AAK, pdev->map_base + I2C_CNTR); |
| } else { |
| /* last byte, don't send acknowledge */ |
| writel(0, pdev->map_base + I2C_CNTR); |
| ack_expected = I2C_DATA_RECEIVE_NACK; |
| } |
| |
| udelay (I2C_DELAY * 5); |
| |
| status = readl(pdev->map_base + I2C_STAT); |
| dev_dbg(pdev, "i2c_get_data len %d status 0x%x\n", len, status); |
| while ((status & 0xff) != ack_expected) { |
| udelay (I2C_DELAY * 10000); |
| if (count > 40) { |
| writel(I2C_STP, pdev->map_base + I2C_CNTR); /*stop */ |
| dev_dbg(pdev, "i2c_get_data timeout status 0x%x\n", status); |
| return 0; |
| } |
| status = readl(pdev->map_base + I2C_STAT); |
| dev_dbg(pdev, "i2c_get_data status 0x%x\n", status); |
| count++; |
| } |
| data = readl(pdev->map_base + I2C_DATA); |
| len--; |
| *return_data = (uchar) data; |
| return_data++; |
| dev_dbg(pdev, "i2c_get_data data 0x%x\n",data); |
| } |
| writel(I2C_STP, pdev->map_base + I2C_CNTR); /* stop */ |
| |
| return (0); |
| } |
| |
| /* created this function to get the i2c_write() */ |
| /* function working properly. */ |
| /* function to write bytes out on the i2c bus */ |
| /* */ |
| /* returns 0 = success */ |
| /* anything other than zero is failure */ |
| static uchar i2c_write_byte (struct device_d *pdev, unsigned char *data, int len) |
| { |
| unsigned int status; |
| int count = 0; |
| unsigned int temp; |
| unsigned char *temp_ptr = data; |
| |
| dev_dbg(pdev, "i2c_write_byte data 0x%x\n", *temp_ptr); |
| |
| while (len) { |
| if (!(len % 128)) |
| printf("#"); |
| /* Set and assert the data */ |
| temp = *temp_ptr; |
| writel(temp, pdev->map_base + I2C_DATA); |
| writel(readl(pdev->map_base + I2C_CNTR) & ~I2C_IFLG, pdev->map_base + I2C_CNTR); |
| |
| udelay (I2C_DELAY*2); |
| |
| status = readl(pdev->map_base + I2C_STAT); |
| count++; |
| while ((status & 0xff) != I2C_DATA_TRANSMIT_ACK) { |
| udelay (I2C_DELAY*2000); |
| if (count > 20) { |
| writel(I2C_STP, pdev->map_base + I2C_CNTR); /*stop */ |
| return (status); |
| } |
| status = readl(pdev->map_base + I2C_STAT); |
| count++; |
| dev_dbg(pdev, "i2c_write_byte status 0x%x\n",status); |
| } |
| len--; |
| temp_ptr++; |
| } |
| |
| return (0); |
| } |
| |
| uchar i2c_read (struct device_d *pdev, uchar dev_addr, uchar * data, int len) |
| { |
| uchar status = 0; |
| |
| status = i2c_start (pdev); |
| |
| if (status) { |
| dev_dbg(pdev, "Transaction start failed: 0x%02x\n", status); |
| return status; |
| } |
| |
| status = i2c_select_device (pdev, dev_addr, 1, 0); /* send the slave address */ |
| if (status) { |
| dev_dbg(pdev, "Address not acknowledged: 0x%02x\n", status); |
| return status; |
| } |
| |
| status = i2c_get_data (pdev, data, len); |
| if (status) { |
| dev_dbg(pdev, "Data not recieved: 0x%02x\n", status); |
| return status; |
| } |
| |
| return 0; |
| } |
| |
| /* Function to set the I2C stop bit */ |
| void i2c_stop (struct device_d *pdev) |
| { |
| writel((0x1 << 4), pdev->map_base + I2C_CNTR); |
| } |
| |
| /* I2C write function */ |
| /* dev_addr = device address */ |
| /* data = pointer to the data to send */ |
| /* len = # of bytes to send */ |
| /* */ |
| /* returns 0 = succesful */ |
| /* anything but zero is failure */ |
| uchar i2c_write (struct device_d *pdev, uchar dev_addr, uchar * data, int len) |
| { |
| uchar status = 0; |
| |
| status = i2c_start (pdev); /* send a start bit */ |
| |
| if (status) { |
| dev_dbg(pdev, "Transaction start failed: 0x%02x\n", status); |
| return status; |
| } |
| |
| status = i2c_select_device (pdev, dev_addr, 0, 0); |
| if (status) { |
| dev_dbg(pdev, "Failed to set slave address: 0x%02x\n", status); |
| return status; |
| } |
| |
| status = i2c_write_byte (pdev, data, len); /* write the data */ |
| if (status) { |
| dev_dbg(pdev, "Data not written: 0x%02x\n", status); |
| return status; |
| } |
| /* issue a stop bit */ |
| i2c_stop (pdev); |
| return 0; |
| } |
| |
| static int i2c_c2k_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) |
| { |
| int i; |
| int result; |
| |
| if (!is_initialized) { |
| i2c_init(adapter->dev); |
| } |
| |
| for (i = 0; i < num; i++) { |
| if (msgs[i].flags & I2C_M_RD) |
| result = i2c_read(adapter->dev, msgs[i].addr, |
| msgs[i].buf, msgs[i].len); |
| else |
| result = i2c_write(adapter->dev, msgs[i].addr, |
| msgs[i].buf, msgs[i].len); |
| |
| if (result) |
| break; |
| } |
| |
| return (result < 0) ? result : num; |
| } |
| |
| /* function to determine if an I2C device is present */ |
| /* chip = device address of chip to check for */ |
| /* */ |
| /* returns 0 = sucessful, the device exists */ |
| /* anything other than zero is failure, no device */ |
| static int i2c_probe (struct device_d *pdev) |
| { |
| uchar status = 0; |
| struct i2c_adapter *adapter; |
| |
| dev_dbg(pdev, "i2c_probe\n"); |
| |
| adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); |
| |
| adapter->master_xfer = i2c_c2k_xfer; |
| adapter->nr = pdev->id; |
| adapter->dev = pdev; |
| |
| /* Add I2C adapter */ |
| status = i2c_add_numbered_adapter(adapter); |
| if (status < 0) |
| { |
| dev_err(pdev, "registration failed\n"); |
| kfree(adapter); |
| return status; |
| } |
| |
| return 0; /* successful completion */ |
| } |
| |
| static struct driver_d c2k_i2c_driver = { |
| .name = "c2k_i2c", |
| .probe = i2c_probe, |
| }; |
| |
| static int c2k_i2c_driverinit(void) |
| { |
| return register_driver(&c2k_i2c_driver); |
| } |
| |
| device_initcall(c2k_i2c_driverinit); |