/* * bt848_i2c.c: * This is an I²C-bus driver for video-cards using the Brooktree Bt848. It * is implemented as a stacked module and can be used by the video-capture * and the videotext-driver. * * Copyright (c) 1997-99 Martin Buck * Read COPYING for more information * * $Id: bt848_i2c.c,v 1.3 1999/10/14 23:20:39 mb Exp mb $ */ #define __KERNEL__ #define MODULE #include #include #if defined CONFIG_MODVERSIONS || defined MODVERSIONS #undef MODVERSIONS #define MODVERSIONS #include #endif #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= 2 * 0x10000 + 1 * 0x100 + 0 #include #endif #include #include "bt848_i2c.h" #define BT848I2C_VER_MAJ 1 #define BT848I2C_VER_MIN 0 #ifndef PCI_VENDOR_ID_BROOKTREE #define PCI_VENDOR_ID_BROOKTREE 0x109e #endif #ifndef PCI_DEVICE_ID_BT848 #define PCI_DEVICE_ID_BT848 0x350 #endif #define BT848_I2C 0x110 #define BT848_GPIO_DMA_CTL 0x10C #define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1) #define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0) #define STRINGIFY_NOMACRO(str) #str #define STRINGIFY(str) STRINGIFY_NOMACRO(str) #define UDELAY(val) udelay(io_delay[!bt848_slow_if][val]) #if LINUX_VERSION_CODE < 2 * 0x10000 + 1 * 0x100 + 0 #define ioremap vremap #define iounmap vfree #undef get_user #define get_user(val, ptr) ((val) = vtx_get_user((ptr), sizeof(*(ptr)))) #undef put_user #define put_user(val, ptr) vtx_put_user((unsigned long)(val), (ptr), sizeof(*(ptr))) extern int vtx_bad_user_access_length(void); static inline long vtx_get_user(const void *ptr, int size) { switch (size) { case 1: return get_fs_byte(ptr); default: vtx_bad_user_access_length(); return 0; } } static inline void vtx_put_user(unsigned long val, void *ptr, int size) { switch (size) { case 1: put_fs_byte(val, ptr); return; default: vtx_bad_user_access_length(); return; } } #endif #if LINUX_VERSION_CODE >= 2 * 0x10000 + 2 * 0x100 + 0 #define RESCHED \ do { \ if (current->need_resched) \ schedule(); \ } while (0) #else #define RESCHED \ do { \ if (need_resched) \ schedule(); \ } while (0) #endif /* This variable is settable when loading the driver */ static int quiet; static int slow_if; static unsigned char *memio_base; static unsigned int io_base; static unsigned char pci_bus, pci_dev_fn; static int save_slow_if, portval; static struct semaphore sem = MUTEX; static const int io_delay[2][3] = { { 3, 5, 7 }, /* Slow speed */ { 2, 3, 5 }, /* Fast speed */ }; static void set_scl(int val) { portval = (portval & ~2) | (val ? 2 : 0); writeb(portval, memio_base + BT848_I2C); } static void set_sda(int val) { portval = (portval & ~1) | (val ? 1 : 0); writeb(portval, memio_base + BT848_I2C); } static int get_sda(void) { return (readb(memio_base + BT848_I2C) & 1) ? 1 : 0; } static void i2c_start(void) { MOD_INC_USE_COUNT; down(&sem); save_slow_if = slow_if; if ((readb(memio_base + BT848_GPIO_DMA_CTL) & (BT848_GPIO_DMA_CTL_RISC_ENABLE | BT848_GPIO_DMA_CTL_FIFO_ENABLE)) == (BT848_GPIO_DMA_CTL_RISC_ENABLE | BT848_GPIO_DMA_CTL_FIFO_ENABLE)) { slow_if = 1; } set_sda(1); set_scl(1); UDELAY(1); set_sda(0); UDELAY(1); set_scl(0); UDELAY(0); } static void i2c_end(void) { set_scl(0); set_sda(0); UDELAY(1); set_scl(1); UDELAY(1); set_sda(1); UDELAY(0); slow_if = save_slow_if; up(&sem); MOD_DEC_USE_COUNT; } static int i2c_sendbyte(int val) { int count, ack; for (count = 7; count >= 0; count--) { set_scl(0); set_sda((val >> count) & 1); UDELAY(1); set_scl(1); UDELAY(1); set_scl(0); UDELAY(0); } set_sda(1); UDELAY(0); set_scl(1); UDELAY(1); ack = !get_sda(); set_scl(0); UDELAY(0); return (ack ? 0 : -1); } static int i2c_getbyte(int ack) { int count, val; set_scl(0); set_sda(1); UDELAY(1); for (val = count = 0; count <= 7; count++) { set_scl(1); UDELAY(2); val = (val << 1) + get_sda(); set_scl(0); UDELAY(1); } set_sda(!ack); UDELAY(1); set_scl(1); UDELAY(2); set_scl(0); UDELAY(0); return val; } int bt848_i2c_senddata(int adr, ...) { int val; va_list argp; va_start(argp, adr); i2c_start(); if (i2c_sendbyte(adr)) goto error; while ((val = va_arg(argp, int)) != -1) { if (val < 0 || val > 255) { printk(KERN_ERR "bt848_i2c: internal error in i2c_senddata: %d\n", val); break; } if (i2c_sendbyte(val)) goto error; } va_end(argp); i2c_end(); return 0; error: i2c_end(); va_end(argp); return -1; } int bt848_i2c_sendbuf(int adr, int reg, int count, unsigned char *buf, int uaccess) { int pos; unsigned char val; i2c_start(); if (i2c_sendbyte(adr)) goto error; if (reg >= 0 && i2c_sendbyte(reg)) goto error; for (pos = 0; pos < count; pos++) { if (uaccess) get_user(val, buf + pos); else val = buf[pos]; if (i2c_sendbyte(val)) goto error; RESCHED; } i2c_end(); return 0; error: i2c_end(); return -1; } int bt848_i2c_readbuf(int adr, int count, unsigned char *buf, int uaccess) { int pos, val; i2c_start(); if (i2c_sendbyte(adr)) goto error; for (pos = 0; pos < count; pos++) { val = i2c_getbyte(pos < count - 1); if (uaccess) { put_user(val, buf + pos); } else { buf[pos] = val; } RESCHED; } i2c_end(); return 0; error: i2c_end(); return -1; } int bt848_slow_if(int val) { int retval = slow_if; slow_if = val; return retval; } void bt848_devinfo(unsigned char **devinfo_memio_base, unsigned int *devinfo_io_base, unsigned char *devinfo_pci_bus, unsigned char *devinfo_pci_dev_fn) { if (devinfo_memio_base) { *devinfo_memio_base = memio_base; } if (devinfo_io_base) { *devinfo_io_base = io_base; } if (devinfo_pci_bus) { *devinfo_pci_bus = pci_bus; } if (devinfo_pci_dev_fn) { *devinfo_pci_dev_fn = pci_dev_fn; } } int init_module(void) { if (!quiet) { printk(KERN_INFO "Bt848 I2C driver version " STRINGIFY(BT848I2C_VER_MAJ) "." STRINGIFY(BT848I2C_VER_MIN) "\n"); } if (!pcibios_present()) { printk(KERN_ERR "bt848_i2c: No PCI-BIOS found\n"); return -EIO; } if (pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, 0, &pci_bus, &pci_dev_fn)) { printk(KERN_ERR "bt848_i2c: PCI device not found\n"); return -EIO; } pcibios_read_config_dword(pci_bus, pci_dev_fn, PCI_BASE_ADDRESS_0, &io_base); memio_base = ioremap(io_base & PCI_BASE_ADDRESS_MEM_MASK, 4096); if (!quiet) { printk(KERN_INFO "bt848_i2c: PCI device at bus %u, device %u, function %u, address 0x%x\n", pci_bus, PCI_SLOT(pci_dev_fn), PCI_FUNC(pci_dev_fn), io_base); } return 0; } void cleanup_module(void) { if (!quiet) { printk(KERN_INFO "bt848_i2c: removing driver\n"); } iounmap(memio_base); }