lkml.org 
[lkml]   [2005]   [Oct]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: PCI driver
Jiri Slaby wrote:

>On 10/10/05, Manu Abraham <manu@linuxtv.org> wrote:
>
>
>>Jiri Slaby wrote:
>>The dprintk() macro (in mantis_common.h ) was looking very badly with
>>wrap, which Andrew also commented on (about the col's) (the macro being
>>the same, eventhough i was using it elsewhere) it being more than 80
>>cols, but wrapping the macro made it look like hell.
>>
>>
>Did you consider using of (inline) function. It would be better in this case.
>[Consider using __attribute__ ((format(...)))]
>
>
>
Ok, that sounds better .. I will take a look at how it looks ..

>>>>>> mantis->pdev = pdev;
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>If you work with this out from pci functions, you should call
>>>>>pci_get_dev and in exit function pci_dev_put, otherwise you don't need
>>>>>it at all.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>Well i am using it in mantis_dma.c, pci_alloc/free
>>>>
>>>>
>>>>
>>>>
>>>And it is called only from places, where pdev is known (i.e. in
>>>parameter of function, e.g. mantis_pci_probe). So you don't need it to
>>>store in mantis, but only call mantis_dma_init(mantis, pdev). Read
>>>below.
>>>
>>>
>>>
>>>
>>>>You mean rather than saving off the pointer, i do a pci_get_dev()
>>>>and later on in the exit routine, i do a pci_dev_put() .. ?
>>>>
>>>>
>>>>
>>>>
>>>But if you really want it, call pci_get_dev() and store it into mantis
>>>struct. In the _device_ exit routine call the latter. But I think,
>>>that not to store is better, or the best is to call
>>>mantis_dma_init(pdev) and do pci_get_drvdata inside.
>>>
>>>
>>>
>>>
>>i think will pass (pdev) it as a function argument. Looks a bit more cleaner
>>But what i fail to understand is , if you can pass it as an argument,
>>why can't you save the pointer in the struct ?
>>
>>
>You can do that. I don't know, how do you handle the mantis structure.
>But if you need pdev in it, you should do (in most cases) pci_dev_get
>to increase ref count = you use it in one more place and telling
>kernel by that: do not free it, because I need it, until I call
>pci_dev_put.
>
>
>

I would need to do that even if i pass it as an argument to the function
, right ?

what i mean is if i do a

dma_init(pdev)

and in

int dma_init(struct pci_dev *pdev)
{
struct x *a = pci_get_drvdata(pdev);

pci_alloc_consistent(pdev, .. );

}

alone would be sufficient ? I went this way as you can see ..


Regards,
Manu

/*
Mantis PCI bridge driver

Copyright (C) 2005 Manu Abraham (manu@kromtek.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef _MANTIS_COMMON_H_
#define _MANTIS_COMMON_H_

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/pci.h>

#include "dvbdev.h"
#include "dvb_demux.h"
#include "dmxdev.h"
#include "dvb_frontend.h"
#include "dvb_net.h"
#include <linux/i2c.h>
#include "mantis_reg.h"

#define MANTIS_ERROR 0
#define MANTIS_NOTICE 1
#define MANTIS_INFO 2
#define MANTIS_DEBUG 3

#define dprintk(x, y, z, format, arg...) do { \
if (z) { \
if ((x > MANTIS_ERROR) && (x > y)) \
printk(KERN_ERR "%s: " format "\n" , __FUNCTION__ , ##arg); \
else if ((x > MANTIS_NOTICE) && (x > y)) \
printk(KERN_NOTICE "%s: " format "\n" , __FUNCTION__ , ##arg); \
else if ((x > MANTIS_INFO) && (x > y)) \
printk(KERN_INFO "%s: " format "\n" , __FUNCTION__ , ##arg); \
else if ((x > MANTIS_DEBUG) && (x > y)) \
printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ##arg); \
} else { \
if (x > y) \
printk(format , ##arg); \
} \
} while(0)

#define mwrite(dat, addr) writel((dat), addr)
#define mread(addr) readl(addr)

#define mmwrite(dat, addr) mwrite((dat), (mantis->mantis_mmio + (addr)))
#define mmread(addr) mread(mantis->mantis_mmio + (addr))
#define mmand(dat, addr) mmwrite((dat) & mmread(addr), addr)
#define mmor(dat, addr) mmwrite((dat) | mmread(addr), addr)
#define mmaor(dat, addr) mmwrite((dat) | ((mask) & mmread(addr)), addr)


struct mantis_pci {
/* PCI stuff */
__u16 vendor_id;
__u16 device_id;
__u8 latency;

unsigned long mantis_addr;
volatile void __iomem *mantis_mmio;

__u8 irq;
__u8 revision;

__u16 mantis_card_num;
__u16 ts_size;

/* RISC Core */
volatile __u32 finished_block;
volatile __u32 last_block;

__u32 block_count;
__u32 block_bytes;
__u32 line_bytes;
__u32 line_count;

__u32 risc_pos;

__u32 buf_size;
__u8 *buf_cpu;
dma_addr_t buf_dma;

__u32 risc_size;
__u32 *risc_cpu;
dma_addr_t risc_dma;

struct tasklet_struct tasklet;

struct i2c_adapter adapter;
int i2c_rc;

/* DVB stuff */
struct dvb_adapter dvb_adapter;
struct dvb_frontend *fe;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net dvbnet;

__u8 feeds;

struct mantis_config *config;

__u32 mantis_int_stat;
__u32 mantis_int_mask;

/* board specific */
__u8 mac_address[8];
__u32 sub_vendor_id;
__u32 sub_device_id;
};

extern unsigned int verbose;
extern int mantis_dvb_init(struct mantis_pci *mantis);
extern int mantis_frontend_init(struct mantis_pci *mantis);
extern int mantis_dvb_exit(struct mantis_pci *mantis);
extern void mantis_dma_xfer(unsigned long data);

#endif
/*
Mantis PCI bridge driver

Copyright (C) 2005 Manu Abraham (manu@kromtek.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "mantis_common.h"
#include "mantis_core.h"


static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
{
int err;
struct i2c_msg msg = {
.addr = 0x50,
.flags = I2C_M_RD,
.buf = data,
.len = length
};
if ((err = i2c_transfer(&mantis->adapter, &msg, 1)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", err, data[0], data[1]);
return err;
}
msleep(2);

return 0;
}

static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
{
int err;
struct i2c_msg msg = {
.addr = 0x50,
.flags = 0,
.buf = data,
.len = length
};
if ((err = i2c_transfer(&mantis->adapter, &msg, 1)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >", err, length, data[0], data[1]);
return err;
}

return 0;
}

static int get_subdevice_id(struct mantis_pci *mantis)
{
int err;
static u8 sub_device_id[2];

mantis->sub_device_id = 0;
sub_device_id[0] = 0xfc;
if ((err = read_eeprom_byte(mantis, &sub_device_id[0], 2)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error");
return err;
}
mantis->sub_device_id = (sub_device_id[0] << 8) | sub_device_id[1];
dprintk(verbose, MANTIS_ERROR, 1, "Sub Device ID=[0x%04x]", mantis->sub_device_id);

return 0;
}

static int get_subvendor_id(struct mantis_pci *mantis)
{
int err;
static u8 sub_vendor_id[2];

mantis->sub_vendor_id = 0;
sub_vendor_id[0] = 0xfe;
if ((err = read_eeprom_byte(mantis, &sub_vendor_id[0], 2)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error");
return err;
}
mantis->sub_vendor_id = (sub_vendor_id[0] << 8) | sub_vendor_id[1];
dprintk(verbose, MANTIS_ERROR, 1, "Sub Vendor ID=[0x%04x]", mantis->sub_vendor_id);

return 0;
}

static int get_mac_address(struct mantis_pci *mantis)
{
int err;

mantis->mac_address[0] = 0x08;
if ((err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error");
return err;
}
dprintk(verbose, MANTIS_ERROR, 1, "MAC Address=[%02x:%02x:%02x:%02x:%02x:%02x]",
mantis->mac_address[0], mantis->mac_address[1], mantis->mac_address[2],
mantis->mac_address[3], mantis->mac_address[4], mantis->mac_address[5]);

return 0;
}


struct vendorname vendorlist[] = {

{
.sub_vendor_name = "Twinhan",
.sub_vendor_id = 0x1822,
},

{ }
};

struct devicetype devicelist[] = {

{
.sub_device_name = "VP-1033",
.sub_device_id = 0x0016,
.device_type = FE_TYPE_SAT,
.type_flags = FE_TYPE_TS204,
},

{ }
};


int __devinit mantis_core_init(struct mantis_pci *mantis)
{
int err;

if ((err = mantis_i2c_init(mantis)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed");
goto err;
}
if ((err = get_mac_address(mantis)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed");
goto err;
}
if ((err = get_subvendor_id(mantis)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "get Sub vendor ID failed");
goto err;
}
if ((err = get_subdevice_id(mantis)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "get Sub device ID failed");
goto err;
}
if ((err = mantis_dvb_init(mantis)) < 0) {
dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed");
goto err;
}

return 0;

err:
return err;
}

int __devexit mantis_core_exit(struct mantis_pci *mantis)
{
if (mantis_dvb_exit(mantis) < 0)
dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed");

if (mantis_i2c_exit(mantis) < 0)
dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed");

return 0;
}

static void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value)
{
u32 reg;

if (value)
reg = 0x0000;
else
reg = 0xffff;

reg = (value << bitpos);

mmwrite(mmread(MANTIS_GPIF_HIFADDR) | reg, MANTIS_GPIF_HIFADDR);
mmwrite(0x00, MANTIS_GPIF_HIFDOUT);
udelay(100);
mmwrite(mmread(MANTIS_GPIF_HIFADDR) | reg, MANTIS_GPIF_HIFADDR);
mmwrite(0x00, MANTIS_GPIF_HIFDOUT);
}

/*
* Tuner power supply control
* Note: this is different from the LNB power control
*
*/
void mantis_fe_powerup(struct mantis_pci *mantis)
{
gpio_set_bits(mantis, 0x0c, 1);
mdelay(100);
gpio_set_bits(mantis, 0x0c, 1);
}

void mantis_fe_powerdown(struct mantis_pci *mantis)
{
gpio_set_bits(mantis, 0x0c, 0);
}
/*
Mantis PCI bridge driver

Copyright (C) 2005 Manu Abraham (manu@kromtek.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef _MANTIS_CORE_H_
#define _MANTIS_CORE_H_

#include "mantis_common.h"


#define FE_TYPE_SAT 0
#define FE_TYPE_CAB 1
#define FE_TYPE_TER 2

#define FE_TYPE_TS204 0
#define FE_TYPE_TS188 1


struct vendorname {
__u8 *sub_vendor_name;
__u32 sub_vendor_id;
};

struct devicetype {
__u8 *sub_device_name;
__u32 sub_device_id;
__u8 device_type;
__u32 type_flags;
};


extern int mantis_dma_init(struct pci_dev *pdev);
extern int mantis_dma_exit(struct pci_dev *pdev);
extern void mantis_dma_start(struct mantis_pci *mantis);
extern void mantis_dma_stop(struct mantis_pci *mantis);
extern int mantis_i2c_init(struct mantis_pci *mantis);
extern int mantis_i2c_exit(struct mantis_pci *mantis);
extern int mantis_core_init(struct mantis_pci *mantis);
extern int mantis_core_exit(struct mantis_pci *mantis);
extern void mantis_fe_powerup(struct mantis_pci *mantis);
extern void mantis_fe_powerdown(struct mantis_pci *mantis);


#endif
/*
Mantis PCI bridge driver

Copyright (C) 2005 Manu Abraham (manu@kromtek.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <asm/page.h>
#include <linux/vmalloc.h>
#include "mantis_common.h"

#define RISC_WRITE (0x01 << 28)
#define RISC_JUMP (0x07 << 28)
#define RISC_SYNC (0x08 << 28)

#define RISC_WR_SOL (1 << 27)
#define RISC_WR_EOL (1 << 26)
#define RISC_IRQ (1 << 24)

#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16))

#define RISC_FLUSH() mantis->risc_pos = 0
#define RISC_INSTR(opcode) mantis->risc_cpu[mantis->risc_pos++] = cpu_to_le32(opcode)


static void mantis_free(struct pci_dev *pdev)
{
struct mantis_pci *mantis = pci_get_drvdata(pdev);

if (mantis->buf_cpu) {
dprintk(verbose, MANTIS_ERROR, 1, "DMA=%lx", (unsigned long) mantis->buf_dma);
pci_free_consistent(pdev, mantis->buf_size, mantis->buf_cpu, mantis->buf_dma);
mantis->buf_cpu = NULL;
}
if (mantis->risc_cpu) {
dprintk(verbose, MANTIS_ERROR, 1, "RISC=%lx", (unsigned long) mantis->risc_dma);
pci_free_consistent(pdev, mantis->risc_size, mantis->risc_cpu, mantis->risc_dma);
mantis->risc_cpu = NULL;
}
}


static int mantis_alloc_buffers(struct pci_dev *pdev)
{
struct mantis_pci *mantis = pci_get_drvdata(pdev);

if (!mantis->buf_cpu) {
mantis->buf_size = 128 * 1024;
mantis->buf_cpu = pci_alloc_consistent(pdev, mantis->buf_size, &mantis->buf_dma);
if (!mantis->buf_cpu) {
dprintk(verbose, MANTIS_ERROR, 1, "DMA buffer allocation failed");
goto err;
}
dprintk(verbose, MANTIS_ERROR, 1, "DMA=0x%lx cpu=0x%p size=%d",
(unsigned long) mantis->buf_dma, mantis->buf_cpu, mantis->buf_size);
}
if (!mantis->risc_cpu) {
mantis->risc_size = PAGE_SIZE;
mantis->risc_cpu = pci_alloc_consistent(pdev, mantis->risc_size, &mantis->risc_dma);
if (!mantis->risc_cpu) {
dprintk(verbose, MANTIS_ERROR, 1, "RISC program allocation failed");
mantis_free(pdev);
goto err;
}
dprintk(verbose, MANTIS_ERROR, 1, "RISC=0x%lx cpu=0x%p size=%d",
(unsigned long) mantis->risc_dma, mantis->risc_cpu, mantis->risc_size);
}

return 0;
err:
dprintk(verbose, MANTIS_ERROR, 1, "Out of memory (?) .....");
return -ENOMEM;
}

static int mantis_calc_lines(struct mantis_pci *mantis)
{
mantis->block_bytes = mantis->buf_size >> 4;
mantis->block_count = 1 << 4;
mantis->line_bytes = mantis->block_bytes;
mantis->line_count = mantis->block_count;

while (mantis->line_bytes > 4095) {
mantis->line_bytes >>= 1;
mantis->line_count <<= 1;
}
dprintk(verbose, MANTIS_DEBUG, 1, "Mantis RISC block bytes=[%d], line bytes=[%d], line count=[%d]",
mantis->block_bytes, mantis->line_bytes, mantis->line_count);

if (mantis->line_count > 255) {
dprintk(verbose, MANTIS_ERROR, 1, "Buffer size error");
return -EINVAL;
}

return 0;
}

static void mantis_risc_program(struct mantis_pci *mantis)
{
u32 buf_pos = 0;
u32 line;

dprintk(verbose, MANTIS_DEBUG, 1, "Mantis create RISC program");
RISC_FLUSH();
RISC_INSTR(RISC_SYNC);
RISC_INSTR(0);

dprintk(verbose, MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u", mantis->line_count, mantis->line_bytes);
for (line = 0; line < mantis->line_count; line++) {
dprintk(verbose, MANTIS_DEBUG, 1, "RISC PROG line=[%d]", line);
if (!(buf_pos % mantis->block_bytes))
RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | RISC_IRQ |
RISC_STATUS(((buf_pos / mantis->block_bytes) +
(mantis->block_count - 1)) % mantis->block_count) | mantis->line_bytes);
else
RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | mantis->line_bytes);
RISC_INSTR(mantis->buf_dma + buf_pos);
buf_pos += mantis->line_bytes;
}
RISC_INSTR(RISC_SYNC);
RISC_INSTR(0);
RISC_INSTR(RISC_JUMP);
RISC_INSTR(mantis->risc_dma);
}

void mantis_dma_start(struct mantis_pci *mantis)
{
dprintk(verbose, MANTIS_DEBUG, 1, "Mantis Start DMA engine");
/*
* Create RISC program to be loaded to the RISC core
*/
mantis_risc_program(mantis);

/*
* Load the RISC program from Host to RISC
*/
mmwrite(cpu_to_le32(mantis->risc_dma), MANTIS_RISC_START);

/*
* Enable INT's, start DMA engine
* DMA engine: Enable FIFO, Enable Data Capture
* Enable RISC engine
*/
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_FIFO_EN | MANTIS_DCAP_EN | MANTIS_RISC_EN, MANTIS_INT_MASK);
mmwrite(mmread(MANTIS_DMA_CTL) | MANTIS_FIFO_EN | MANTIS_DCAP_EN | MANTIS_RISC_EN, MANTIS_DMA_CTL);

}

void mantis_dma_stop(struct mantis_pci *mantis)
{
dprintk(verbose, MANTIS_DEBUG, 1, "Mantis Stop DMA engine");

/*
* Stop only the DMA engine, do not clear
* the other interrupts
*/
mmwrite((mmread(MANTIS_DMA_CTL) & ~(MANTIS_FIFO_EN | MANTIS_DCAP_EN | MANTIS_RISC_EN)), MANTIS_DMA_CTL);
}


int __devinit mantis_dma_init(struct pci_dev *pdev)
{
int err = 0;
struct mantis_pci *mantis = pci_get_drvdata(pdev);

dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DMA init");
if (mantis_alloc_buffers(pdev) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Error allocating DMA buffer");

/*
* Disable all interrupts during initialization
* to avoid spurious interrupts creating hell.
* Clear all INT's, disable FIFO RISC.
*/
mmwrite(0x00, MANTIS_INT_MASK);
mmwrite(0x00, MANTIS_DMA_CTL);

goto err;
}
if ((err = mantis_calc_lines(mantis)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis calc lines failed");
goto err;
}

return 0;
err:
return err;
}

void mantis_dma_xfer(unsigned long data)
{
struct mantis_pci *mantis = (struct mantis_pci *) data;

while (mantis->last_block != mantis->finished_block) {
(mantis->ts_size ? dvb_dmx_swfilter_204: dvb_dmx_swfilter)
(&mantis->demux, &mantis->buf_cpu[mantis->last_block * mantis->block_bytes], mantis->block_bytes);
mantis->last_block = (mantis->last_block + 1) % mantis->block_count;
}
}

int __devexit mantis_dma_exit(struct pci_dev *pdev)
{
struct mantis_pci *mantis = pci_get_drvdata(pdev);

if (mantis->buf_cpu) {
dprintk(verbose, MANTIS_ERROR, 1, "DMA=0x%lx cpu=0x%p size=%d",
(unsigned long) mantis->buf_dma, mantis->buf_cpu, mantis->buf_size);
pci_free_consistent(pdev, mantis->buf_size, mantis->buf_cpu, mantis->buf_dma);
mantis->buf_cpu = NULL;
}
if (mantis->risc_cpu) {
dprintk(verbose, MANTIS_ERROR, 1, "RISC=0x%lx cpu=0x%p size=%d",
(unsigned long) mantis->risc_dma, mantis->risc_cpu, mantis->risc_size);
pci_free_consistent(pdev, mantis->risc_size, mantis->risc_cpu, mantis->risc_dma);
mantis->risc_cpu = NULL;
}

return 0;
}
/*
Mantis PCI bridge driver

Copyright (C) 2005 Manu Abraham (manu@kromtek.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <linux/bitops.h>
#include "mantis_common.h"
#include "mantis_core.h"

#include "dmxdev.h"
#include "dvbdev.h"
#include "dvb_demux.h"
#include "dvb_frontend.h"
#include "stv0299.h"
/*
0x01, 0x15,
0x02, 0x00, // 0x30
0x03, 0x00,
0x04, 0x2a, // 0x7d
0x05, 0x85, // 0x35
0x06, 0x02,
0x07, 0x00,
0x08, 0x00, // 0xc3
0x0C, 0x01, // 0x00
0x0D, 0x81,
0x0E, 0x23, // 0x23
0x0F, 0x12, // 0x12
0x10, 0x7e, // 0x7e
0x11, 0x84,
0x12, 0xB9,
0x13, 0x88, // 0x88
0x14, 0x89, // 0x89
0x15, 0xC9,
0x16, 0x00, // 0x00
0x17, 0x5c, // 0x5c
0x18, 0x00, // 0x00
0x19, 0x00, // 0x00
0x1A, 0x00,
0x1C, 0x00,
0x1D, 0x00,
0x1E, 0x00,
0x1F, 0x3A,
0x20, 0x2E,
0x21, 0x80,
0x22, 0xFF,
0x23, 0xC1,
0x28, 0x00,
0x29, 0x1E,
0x2A, 0x14,
0x2B, 0x0F,
0x2C, 0x09,
0x2D, 0x05,
0x31, 0x1F,
0x32, 0x19,
0x33, 0xFE,
0x34, 0x93,
0xff, 0xff,

*/

static u8 lgtdqcs001f_inittab[] = {
0x01, 0x15,
0x02, 0x00,
0x03, 0x00,
0x04, 0x2a,
0x05, 0x85,
0x06, 0x02,
0x07, 0x00,
0x08, 0x00,
0x0c, 0x01,
0x0d, 0x81,
0x0e, 0x44,
// 0x0f, 0x14,
0x0f, 0x94,
0x10, 0x3c,
0x11, 0x84,
0x12, 0xb9,
0x13, 0xb5,
0x14, 0x4f,
0x15, 0xc9,
0x16, 0x80,
0x17, 0x36,
0x18, 0xfb,
0x19, 0xcf,
0x1a, 0xbc,
0x1c, 0x2b,
0x1d, 0x27,
0x1e, 0x00,
0x1f, 0x0b,
0x20, 0xa1,
0x21, 0x60,
0x22, 0x00,
0x23, 0x00,
0x28, 0x00,
0x29, 0x28,
0x2a, 0x14,
0x2b, 0x0f,
0x2c, 0x09,
0x2d, 0x05,
0x31, 0x1f,
0x32, 0x19,
0x33, 0xfc,
0x34, 0x13,
0xff, 0xff,
};

static int lgtdqcs001f_pll_set(struct dvb_frontend* fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters* params)
{
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
// struct i2c_msg msg_r = { .addr = 0x61, .flags = I2C_M_RD, .buf = buf, .len = 1 };

div = params->frequency / 250;

buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = 0x83;
buf[3] = 0xc0;

if (params->frequency < 1531000)
buf[3] |= 0x04;
else
buf[3] &= ~0x04;

if (i2c_transfer(i2c, &msg, 4) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Write: I2C Transfer failed");
return -EIO;
}
msleep(100);
/*
if (i2c_transfer(i2c, &msg_r, 1) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Read: I2C Transfer failed");
return -EIO;
}
dprintk(verbose, MANTIS_ERROR, 1, "TSA5059 read=[0x%02x]", buf[0]);
*/

return 0;
}

static int lgtdqcs001f_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
{
u8 aclk = 0;
u8 bclk = 0;

if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }

stv0299_writereg (fe, 0x13, aclk);
stv0299_writereg (fe, 0x14, bclk);

stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
stv0299_writereg (fe, 0x20, (ratio >> 8) & 0xff);
stv0299_writereg (fe, 0x21, (ratio ) & 0xf0);

return 0;
}


static struct stv0299_config lgtdqcs001f_config = {

.demod_address = 0x68,
.inittab = lgtdqcs001f_inittab,
.mclk = 88000000UL,
// .invert = 0,
.invert = 1,
// .enhanced_tuning = 0,
.skip_reinit = 0,
.lock_output = STV0229_LOCKOUTPUT_0,
.volt13_op0_op1 = STV0299_VOLT13_OP0,
.min_delay_ms = 100,
.set_symbol_rate = lgtdqcs001f_set_symbol_rate,
.pll_set = lgtdqcs001f_pll_set,
};


static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
struct mantis_pci *mantis = dvbdmx->priv;

dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB Start feed");
if (!dvbdmx->dmx.frontend) {
dprintk(verbose, MANTIS_DEBUG, 1, "no frontend ?");
return -EINVAL;
}
mantis->feeds++;
dprintk(verbose, MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds);
if (mantis->feeds == 1) {
dprintk(verbose, MANTIS_DEBUG, 1, "mantis start feed & dma");
mantis_dma_start(mantis);
}

return mantis->feeds;
}

static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
struct mantis_pci *mantis = dvbdmx->priv;

dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB Stop feed");
if (!dvbdmx->dmx.frontend) {
dprintk(verbose, MANTIS_DEBUG, 1, "no frontend ?");
return -EINVAL;
}
mantis->feeds--;
if (mantis->feeds == 0) {
dprintk(verbose, MANTIS_DEBUG, 1, "mantis stop feed and dma");
mantis_dma_stop(mantis);
}

return 0;
}

int __devinit mantis_dvb_init(struct mantis_pci *mantis)
{
int result;
dprintk(verbose, MANTIS_DEBUG, 1, "dvb_register_adapter");
if (dvb_register_adapter(&mantis->dvb_adapter, "Mantis dvb adapter", THIS_MODULE) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Error registering adapter");
return -ENODEV;
}
mantis->dvb_adapter.priv = mantis;
mantis->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING;
mantis->demux.priv = mantis;
mantis->demux.filternum = 256;
mantis->demux.feednum = 256;
mantis->demux.start_feed = mantis_dvb_start_feed;
mantis->demux.stop_feed = mantis_dvb_stop_feed;
mantis->demux.write_to_decoder = NULL;

mantis->ts_size = 1;
dprintk(verbose, MANTIS_DEBUG, 1, "dvb_dmx_init");
if ((result = dvb_dmx_init(&mantis->demux)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
goto err0;
}
mantis->dmxdev.filternum = 256;
mantis->dmxdev.demux = &mantis->demux.dmx;
mantis->dmxdev.capabilities = 0;
dprintk(verbose, MANTIS_DEBUG, 1, "dvb_dmxdev_init");
if ((result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result);
goto err1;
}
mantis->fe_hw.source = DMX_FRONTEND_0;
if ((result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
goto err2;
}
mantis->fe_mem.source = DMX_MEMORY_FE;
if ((result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
goto err3;
}
if ((result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
goto err4;
}
dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx);
tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis);
mantis_frontend_init(mantis);

return 0;

/*
* Error conditions ..
*/

err4:
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
err3:
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
err2:
dvb_dmxdev_release(&mantis->dmxdev);
err1:
dvb_dmx_release(&mantis->demux);
err0:
dvb_unregister_adapter(&mantis->dvb_adapter);

return result;
}

int __devinit mantis_frontend_init(struct mantis_pci *mantis)
{
dprintk(verbose, MANTIS_DEBUG, 1, "Mantis frontend Init");
mantis_fe_powerup(mantis);

switch (mantis->sub_device_id) {
case 0x0016: /* VP-1033 */
dprintk(verbose, MANTIS_DEBUG, 1, "Searching for a STV0299 (DVB-S) frontend");
mantis->fe = stv0299_attach(&lgtdqcs001f_config, &mantis->adapter);
if (mantis->fe) {
dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB-S STV0299 frontend attach success");
dprintk(verbose, MANTIS_ERROR, 1, "found STV0299 DVB-S frontend @ 0x%02x", lgtdqcs001f_config.demod_address);
}
break;
default:
dprintk(verbose, MANTIS_DEBUG, 1, "Unknown frontend type:[0x%02x]", mantis->sub_device_id);
return -ENODEV;
}
if (mantis->fe == NULL)
dprintk(verbose, MANTIS_ERROR, 1, "A frontend was not found for the device");
else {
if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) {
dprintk(verbose, MANTIS_ERROR, 1, "ERROR: Frontend registration failed");
if (mantis->fe->ops->release)
mantis->fe->ops->release(mantis->fe);
mantis->fe = NULL;
return -ENODEV;
}
}

return 0;
}

int __devexit mantis_dvb_exit(struct mantis_pci *mantis)
{
tasklet_kill(&mantis->tasklet);
dvb_net_release(&mantis->dvbnet);
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
dvb_dmxdev_release(&mantis->dmxdev);
dvb_dmx_release(&mantis->demux);
if (mantis->fe)
dvb_unregister_frontend(mantis->fe);
dprintk(verbose, MANTIS_DEBUG, 1, "dvb_unregister_adapter");
dvb_unregister_adapter(&mantis->dvb_adapter);

return 0;
}
/*
Mantis PCI bridge driver

Copyright (C) 2005 Manu Abraham (manu@kromtek.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include "mantis_common.h"

#define I2C_HW_B_MANTIS 0x1c

/*
* Wait till we receive INT I2CRACK. I2CDONE will follow suit,
* since we save the states, we would have both I2CRACK and I2CDONE set
* we don't wait till eternity though.
*/
static int mantis_ack_wait(struct mantis_pci *mantis)
{
u8 timeout = 0;

/*
* I2CRACK will be reset when I2CDONE is set
*
*
*/
while (!(mantis->mantis_int_stat & MANTIS_INT_I2CRACK)) {
udelay(1000);
timeout++;
dprintk(verbose, MANTIS_DEBUG, 0, ".");
if (timeout > 50)
return -1;
}

/*
* Reset STATUS flags, before we leave.
* since we save states.
*/
dprintk(verbose, MANTIS_DEBUG, 1, "Status ... MANTIS_INT_STAT=[0x%08x]", mantis->mantis_int_stat);
mantis->mantis_int_stat &= (~MANTIS_INT_I2CRACK | ~MANTIS_INT_I2CDONE);

return 0;
}

static inline void mantis_i2cint_set(struct mantis_pci *mantis)
{
/*
* We don't have a mask for I2CRACK
* I2CRACK is cleared on setting I2CDONE (?)
*/
// mmwrite(mmread(MANTIS_INT_STAT) & MANTIS_INT_I2CRACK, MANTIS_INT_STAT);
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK);
udelay(1000);
}


static int mantis_i2c_pagewrite(struct mantis_pci *mantis, struct i2c_msg *msg)
{
u8 i;
u32 txd = 0;

dprintk(verbose, MANTIS_DEBUG, 1, "Writing to [0x%02x]", msg->addr);

for (i = 0; i < msg->len; i++) {
dprintk(verbose, MANTIS_DEBUG, 1, "Data<W[%d]>=[0x%02x]", i, msg->buf[i]);
txd |= (msg->addr << 25) | (msg->buf[i] << 8) | MANTIS_I2C_RATE_3 | MANTIS_I2C_STOP | MANTIS_I2C_PGMODE;
if (i == 3)
txd &= ~MANTIS_I2C_STOP;

mantis_i2cint_set(mantis);
mmwrite(txd, MANTIS_I2CDATA_CTL);

if (mantis_ack_wait(mantis) < 0) {
dprintk(verbose, MANTIS_DEBUG, 1, "ACK failed");
return -1;
}
udelay(10);
}

return 0;
}

/* FIXME! needs to be fixed, we need to put things back */
static int mantis_i2c_pageread(struct mantis_pci *mantis, struct i2c_msg *msg)
{
u8 i;
u32 rxd = 0;

for (i = 0; i < 2; i++) {
rxd |= ((msg->addr << 25) | (1 << 24)) | MANTIS_I2C_RATE_3 | MANTIS_I2C_STOP | MANTIS_I2C_PGMODE;
if (i == 1)
rxd &= ~MANTIS_I2C_STOP;
mmwrite(mmread(MANTIS_INT_STAT) | MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK);
udelay(1000);
if (mantis_ack_wait(mantis) < 0)
return -EIO;
rxd = mmread(MANTIS_I2CDATA_CTL);
udelay(500);
}

return 0;
}

///* FIXME ! Right now writes only a byte */
static int mantis_i2c_writebyte(struct mantis_pci *mantis, struct i2c_msg *msg)
{
u32 txd = 0;
mantis->mantis_int_stat = 0;

txd = (msg->addr << 25) | (msg->buf[0] << 16) | (msg->buf[1] << 8) | MANTIS_I2C_RATE_3;
dprintk(verbose, MANTIS_DEBUG, 1, "Writing to [0x%02x], Data<W>=[%02x]", msg->addr, msg->buf[1]);
mmwrite(txd, MANTIS_I2CDATA_CTL);

if (mantis_ack_wait(mantis) < 0) {
dprintk(verbose, MANTIS_DEBUG, 1, "Slave did not ACK !");
return -EIO;
}

return 0;
}

static int mantis_i2c_readbyte(struct mantis_pci *mantis, struct i2c_msg *msg)
{
u32 rxd;
u16 i;
u8 subaddr;

/*
* Clear saved previous states
*/
mantis->mantis_int_stat &= (~MANTIS_INT_I2CDONE | ~MANTIS_INT_I2CRACK);
subaddr = msg->buf[0];

for (i = 0; i < msg->len; i++) {
/* Rate settings */
rxd = ((msg->addr << 25) | (1 << 24)) | (subaddr << 16) | MANTIS_I2C_RATE_3;

mmwrite(rxd, MANTIS_I2CDATA_CTL);

/*
* Enable INT_I2CDONE
*/
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK);
udelay(1000);

/* Slave ACK */
if (mantis_ack_wait(mantis) < 0)
return -EIO;

rxd = mmread(MANTIS_I2CDATA_CTL);
udelay(500);
msg->buf[i] = (rxd >> 8) & 0xff;
dprintk(verbose, MANTIS_DEBUG, 1, "Reading from [0x%02x], Data<R[%d]>=[0x%02x]", msg->addr, i, msg->buf[i]);
subaddr++;
}

return 0;
}

static int mantis_i2c_readbytes(struct mantis_pci *mantis, struct i2c_msg *msg)
{
u32 rxd;
u16 i;

mantis->mantis_int_stat = 0;

for (i = 0; i < msg[1].len; i++) {
/* Rate settings */
rxd = ((msg[0].addr << 25) | (1 << 24)) | (msg[0].buf[0] << 16) | MANTIS_I2C_RATE_3;

mmwrite(rxd, MANTIS_I2CDATA_CTL);

/*
* Need to wait till I2CDONE, rather than slave I2CRACK
*/
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK);
udelay(1000);
if (mantis_ack_wait(mantis) < 0)
return -EIO;

rxd = mmread(MANTIS_I2CDATA_CTL);
udelay(500);
msg[1].buf[i] = (rxd >> 8) & 0xff;
dprintk(verbose, MANTIS_DEBUG, 1, "Reading from [0x%02x], Data<R[%d]>=[0x%02x]", msg[0].addr, i, msg[1].buf[i]);
msg[0].buf[0]++;
}

return 0;
}

/* FIXME ! Right now writes only a byte */
static int mantis_i2c_writebytes(struct mantis_pci *mantis, struct i2c_msg *msg)
{
u32 txd = 0;
mantis->mantis_int_stat = 0;

txd = (msg->addr << 25) | (msg->buf[0] << 16) | (msg->buf[1] << 8) | MANTIS_I2C_RATE_3;
dprintk(verbose, MANTIS_DEBUG, 1, "Writing to [0x%02x], Data<W>=[%02x]", msg->addr, msg->buf[1]);
mmwrite(txd, MANTIS_I2CDATA_CTL);

if (mantis_ack_wait(mantis) < 0)
return -EIO;

return 0;
}

static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg, int num)
{
int retval = 0;
struct mantis_pci *mantis;

mantis = i2c_get_adapdata(adapter);

if (num == 4) {
if (msg[0].flags & I2C_M_RD) {
if ((retval = mantis_i2c_pageread(mantis, msg)) < 0)
return retval;
} else {
if ((retval = mantis_i2c_pagewrite(mantis, msg)) < 0)
return retval;
}
/* STV0299 operation (???) */
} else if (num == 2) {
mantis_i2cint_set(mantis);
/* Sanity check */
if (msg[0].buf != NULL && msg[1].buf != NULL) {
/* I2C operation type */
if (msg[0].flags == 0 && msg[1].flags == I2C_M_RD) {
/* Read */
if ((retval = mantis_i2c_readbytes(mantis, msg)) < 0)
return retval;
} else {
/* Write */
if ((retval = mantis_i2c_writebytes(mantis, msg)) < 0)
return retval;
}
}
/* Normal case operation */
} else {
mantis_i2cint_set(mantis);
if (msg[0].flags & I2C_M_RD) {
if ((retval = mantis_i2c_readbyte(mantis, msg)) < 0)
return retval;
} else {
if ((retval = mantis_i2c_writebyte(mantis, msg)) < 0)
return retval;
}
}

return num;
}

static __u32 mantis_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_EMUL;
}

static int mantis_attach_inform(struct i2c_client *client)
{
struct mantis_pci *mantis;

mantis = i2c_get_adapdata(client->adapter);
dprintk(verbose, MANTIS_ERROR, 1, "mantis: %s i2c attach [addr = 0x%x, client=%s]",
client->driver->name, client->addr, i2c_clientname(client));

return 0;
}

static struct i2c_algorithm mantis_algo = {
.name = "Mantis I2C",
.id = I2C_HW_B_MANTIS,
.master_xfer = mantis_i2c_xfer,
.functionality = mantis_i2c_func,
};

static struct i2c_adapter mantis_i2c_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_TV_DIGITAL,
.algo = &mantis_algo,
.client_register = mantis_attach_inform,
};

int __devinit mantis_i2c_init(struct mantis_pci *mantis)
{
memcpy(&mantis->adapter, &mantis_i2c_adapter, sizeof (mantis_i2c_adapter));
i2c_set_adapdata(&mantis->adapter, mantis);
mantis->i2c_rc = i2c_add_adapter(&mantis->adapter);
if (mantis->i2c_rc < 0)
return mantis->i2c_rc;

dprintk(verbose, MANTIS_DEBUG, 1, "Initializing I2C ..");

/*
* I2CRACK will be reset when I2CDONE is set
* Clear and setup I2C int mask
*/
mmwrite(mmread(MANTIS_INT_STAT) & ( ~MANTIS_INT_I2CDONE | ~MANTIS_INT_I2CRACK), MANTIS_INT_STAT);
mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE | MANTIS_INT_I2CRACK, MANTIS_INT_MASK);
dprintk(verbose, MANTIS_DEBUG, 1, "INT_STAT=[0x%08x], INT_MASK=[0x%08x]", mmread(MANTIS_INT_STAT), mmread(MANTIS_INT_MASK));

return 0;
}

int __devexit mantis_i2c_exit(struct mantis_pci *mantis)
{
dprintk(verbose, MANTIS_DEBUG, 1, "Removing I2C adapter");
return i2c_del_adapter(&mantis->adapter);
}
/*
Mantis PCI bridge driver

Copyright (C) 2005 Manu Abraham (manu@kromtek.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/device.h>
#include "mantis_common.h"
#include "mantis_core.h"

#include <asm/irq.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>

unsigned int verbose = 1;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");

#define PCI_VENDOR_ID_MANTIS 0x1822
#define PCI_DEVICE_ID_MANTIS_R11 0x4e35
#define DRIVER_NAME "Mantis"

static struct pci_device_id mantis_pci_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MANTIS, PCI_DEVICE_ID_MANTIS_R11) },
{ 0 },
};

MODULE_DEVICE_TABLE(pci, mantis_pci_table);

static irqreturn_t mantis_pci_irq(int irq, void *dev_id, struct pt_regs *regs)
{
int count = 0, interrupts = 0;
u32 stat = 0, mask = 0, temp;

struct mantis_pci *mantis = dev_id;

if (mantis == NULL)
dprintk(verbose, MANTIS_DEBUG, 1, "Aeio, mantis ISR");

/*
* Don't read each and everytime, but read in one shot
* We need to save the states for later too ..
*
*/

stat = mmread(MANTIS_INT_STAT);
mask = mmread(MANTIS_INT_MASK);

/*
* To speed up things, do a basic check whether it is our
* interrupt.
* Also, we don't have a mask for I2CRACK
*/
if (!(stat & (mask | MANTIS_INT_I2CRACK)) ) {
dprintk(verbose, MANTIS_ERROR, 1, "Not ours !");
return IRQ_NONE;
}

/*
* The int is for us, clear int condition
*/
mmwrite(stat, MANTIS_INT_STAT);

/*
* Check how many 1's are in the INT_STAT, Count will reflect
* no. of interrupts, we should loop count times.
*
*/
temp = stat;
while (temp) {
if (temp & 0x01) {
interrupts++;
dprintk(verbose, MANTIS_DEBUG, 1, "Interrupt @ %d", interrupts);
}
temp >>= 1;
count++;

/*
* We should never loop more than reg. width
* else, Bail out
*/
if (count > 32) {
dprintk(verbose, MANTIS_ERROR, 1, "Bailing out !!");
break;
}
}
count = 0;
while (count < interrupts) {
if ((stat & mask) & MANTIS_INT_RISCEN) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** DMA enabl ****");
mantis->mantis_int_stat |= MANTIS_INT_RISCEN;
stat &= ~MANTIS_INT_RISCEN;

/*
* It's a shame that there is no mask for I2CRACK !
*/
} else if (stat & MANTIS_INT_I2CRACK) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** I2C R-ACK ****");
mantis->mantis_int_stat |= MANTIS_INT_I2CRACK;
stat &= ~MANTIS_INT_I2CRACK;

} else if ((stat & mask) & MANTIS_INT_IRQ0) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** INT IRQ-0 ****");
mantis->mantis_int_stat |= MANTIS_INT_IRQ0;
stat &= ~MANTIS_INT_IRQ0;

} else if ((stat & mask) & MANTIS_INT_IRQ1) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** INT IRQ-1 ****");
mantis->mantis_int_stat |= MANTIS_INT_IRQ1;
stat &= ~MANTIS_INT_IRQ1;

} else if ((stat & mask) & MANTIS_INT_OCERR) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** INT OCERR ****");
mantis->mantis_int_stat |= MANTIS_INT_OCERR;
stat &= ~MANTIS_INT_OCERR;

} else if ((stat & mask) & MANTIS_INT_PABORT) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** INT PABRT ****");
mantis->mantis_int_stat |= MANTIS_INT_PABORT;
stat &= ~MANTIS_INT_PABORT;

} else if ((stat & mask) & MANTIS_INT_RIPERR) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** INT RIPRR ****");
mantis->mantis_int_stat |= MANTIS_INT_RIPERR;
stat &= ~MANTIS_INT_RIPERR;

} else if ((stat & mask) & MANTIS_INT_PPERR) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** INT PPERR ****");
mantis->mantis_int_stat |= MANTIS_INT_PPERR;
stat &= ~MANTIS_INT_PPERR;

} else if ((stat & mask) & MANTIS_INT_FTRGT) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** INT FTRGT ****");
mantis->mantis_int_stat |= MANTIS_INT_FTRGT;
stat &= ~MANTIS_INT_FTRGT;

} else if ((stat & mask) & MANTIS_INT_RISCI) {
mantis->finished_block = (mantis->mantis_int_stat & MANTIS_INT_RISCSTAT) >> 28;
tasklet_schedule(&mantis->tasklet);
stat &= ~MANTIS_INT_RISCI;

} else if ((stat & mask) & MANTIS_INT_I2CDONE) {
dprintk(verbose, MANTIS_DEBUG, 1, "**** I2C DONE ****");
mantis->mantis_int_stat |= MANTIS_INT_I2CDONE;
stat &= ~MANTIS_INT_I2CDONE;

} else {
dprintk(verbose, MANTIS_DEBUG, 1, "Unknown INT ???");
}

count++;

/*
* Being paranoid, check whether we are too loopy !
*/
if (count > 32) {
dprintk(verbose, MANTIS_ERROR, 1, "IRQ Lockup, clearing INT mask");
dprintk(verbose, MANTIS_ERROR, 1, "Count=%d, Interrupts=%d, stat=[0x%08x]", count, interrupts, stat);
mmwrite(0, MANTIS_INT_MASK);
stat = 0, interrupts = 0;

break;
}
}

return IRQ_HANDLED;
}


static int __devinit mantis_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *mantis_pci_table)
{
u8 revision, latency;
struct mantis_pci *mantis;

if (pci_enable_device(pdev)) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis PCI enable failed");
goto exit;
}

/*
* We need to change this to kzalloc on newer kernels
* kzalloc() = kmalloc() + memset()
* Code simplification for 2.6.14 and upwards
*/
mantis = kmalloc(sizeof (struct mantis_pci), GFP_KERNEL);
// mantis = kzalloc(sizeof (struct mantis_pci), GFP_KERNEL);
if (mantis == NULL) {
dprintk(verbose, MANTIS_ERROR, 1, "Out of memory");
goto disable;
}
memset(mantis, 0, sizeof (struct mantis_pci));

mantis->mantis_addr = pci_resource_start(pdev, 0);
if (!request_mem_region(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0), DRIVER_NAME)) {
goto free;
}

if ((mantis->mantis_mmio =
ioremap(mantis->mantis_addr, 0x1000)) == NULL) {
dprintk(verbose, MANTIS_ERROR, 1, "IO remap failed");
goto release;
}

/*
* Clear and disable all interrupts at startup
* to avoid lockup situations
*/
mmwrite(0x00, MANTIS_INT_STAT);
mmwrite(0x00, MANTIS_INT_MASK);
if (request_irq(pdev->irq, mantis_pci_irq, SA_SHIRQ | SA_INTERRUPT,
DRIVER_NAME, mantis) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis IRQ reg failed");
goto unmap;
}
pci_set_master(pdev);
pci_set_drvdata(pdev, mantis);
pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency);
pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
mantis->latency = latency;
mantis->revision = revision;

/*
* Setup default latency 32 if none specified
*/
if (!latency)
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 32);

printk("%s: Mantis Rev %d, ", __func__, mantis->revision);
printk("irq: %d, latency: %d\nmemory: 0x%lx, mmio: 0x%p\n",
pdev->irq, mantis->latency, mantis->mantis_addr,
mantis->mantis_mmio);

if ((mantis_dma_init(pdev)) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed");
goto disable;
}

/*
* No more PCI specific stuff !
*/
if (mantis_core_init(mantis) < 0) {
dprintk(verbose, MANTIS_ERROR, 1, "Mantis core init failed");
goto dma_exit;
}

return 0;

/*
* Error conditions ..
*/

dma_exit:
if (mantis_dma_exit(pdev) < 0)
dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA exit failed");

dprintk(verbose, MANTIS_DEBUG, 1, "Err: IO Unmap");
unmap:
if (mantis->mantis_mmio)
iounmap(mantis->mantis_mmio);
release:
dprintk(verbose, MANTIS_DEBUG, 1, "Err: Release regions");
release_mem_region(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
free:
kfree(mantis);

disable:
pci_disable_device(pdev);
dprintk(verbose, MANTIS_DEBUG, 1, "Err: Disabling device");
exit:
dprintk(verbose, MANTIS_DEBUG, 1, "Err:");
return -ENODEV;
}

static void __devexit mantis_pci_remove(struct pci_dev *pdev)
{
struct mantis_pci *mantis = pci_get_drvdata(pdev);
if (mantis == NULL) {
dprintk(verbose, MANTIS_ERROR, 1, "Aeio, Mantis NULL ptr");
return;
}
mantis_core_exit(mantis);

printk("%s: Mantis irq: %d,latency: %d\n memory: 0x%lx, mmio: 0x%p",
__func__, pdev->irq, mantis->latency, mantis->mantis_addr,
mantis->mantis_mmio);

free_irq(pdev->irq, mantis);
pci_release_regions(pdev);
if (mantis_dma_exit(pdev) < 0)
dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed");

pci_set_drvdata(pdev, NULL);
pci_disable_device(pdev);
kfree(mantis);
}

static struct pci_driver mantis_pci_driver = {
.name = DRIVER_NAME,
.id_table = mantis_pci_table,
.probe = mantis_pci_probe,
.remove = __devexit_p(mantis_pci_remove),
};

static int __init mantis_pci_init(void)
{
return pci_register_driver(&mantis_pci_driver);
}

static void __exit mantis_pci_exit(void)
{
pci_unregister_driver(&mantis_pci_driver);
}

module_init(mantis_pci_init);
module_exit(mantis_pci_exit);

MODULE_DESCRIPTION("Mantis PCI DTV bridge driver");
MODULE_AUTHOR("Manu Abraham");
MODULE_LICENSE("GPL");
\
 
 \ /
  Last update: 2005-10-10 20:44    [W:1.451 / U:0.152 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site