forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
4.6 KiB
208 lines
4.6 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* matrox_w1.c |
|
* |
|
* Copyright (c) 2004 Evgeniy Polyakov <[email protected]> |
|
*/ |
|
|
|
#include <asm/types.h> |
|
#include <linux/atomic.h> |
|
#include <asm/io.h> |
|
|
|
#include <linux/delay.h> |
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
#include <linux/list.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/spinlock.h> |
|
#include <linux/timer.h> |
|
#include <linux/slab.h> |
|
#include <linux/pci_ids.h> |
|
#include <linux/pci.h> |
|
|
|
#include <linux/w1.h> |
|
|
|
/* |
|
* Matrox G400 DDC registers. |
|
*/ |
|
|
|
#define MATROX_G400_DDC_CLK (1<<4) |
|
#define MATROX_G400_DDC_DATA (1<<1) |
|
|
|
#define MATROX_BASE 0x3C00 |
|
#define MATROX_STATUS 0x1e14 |
|
|
|
#define MATROX_PORT_INDEX_OFFSET 0x00 |
|
#define MATROX_PORT_DATA_OFFSET 0x0A |
|
|
|
#define MATROX_GET_CONTROL 0x2A |
|
#define MATROX_GET_DATA 0x2B |
|
#define MATROX_CURSOR_CTL 0x06 |
|
|
|
struct matrox_device |
|
{ |
|
void __iomem *base_addr; |
|
void __iomem *port_index; |
|
void __iomem *port_data; |
|
u8 data_mask; |
|
|
|
unsigned long phys_addr; |
|
void __iomem *virt_addr; |
|
unsigned long found; |
|
|
|
struct w1_bus_master *bus_master; |
|
}; |
|
|
|
/* |
|
* These functions read and write DDC Data bit. |
|
* |
|
* Using tristate pins, since i can't find any open-drain pin in whole motherboard. |
|
* Unfortunately we can't connect to Intel's 82801xx IO controller |
|
* since we don't know motherboard schema, which has pretty unused(may be not) GPIO. |
|
* |
|
* I've heard that PIIX also has open drain pin. |
|
* |
|
* Port mapping. |
|
*/ |
|
static __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg) |
|
{ |
|
u8 ret; |
|
|
|
writeb(reg, dev->port_index); |
|
ret = readb(dev->port_data); |
|
barrier(); |
|
|
|
return ret; |
|
} |
|
|
|
static __inline__ void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val) |
|
{ |
|
writeb(reg, dev->port_index); |
|
writeb(val, dev->port_data); |
|
wmb(); |
|
} |
|
|
|
static void matrox_w1_write_ddc_bit(void *data, u8 bit) |
|
{ |
|
u8 ret; |
|
struct matrox_device *dev = data; |
|
|
|
if (bit) |
|
bit = 0; |
|
else |
|
bit = dev->data_mask; |
|
|
|
ret = matrox_w1_read_reg(dev, MATROX_GET_CONTROL); |
|
matrox_w1_write_reg(dev, MATROX_GET_CONTROL, ((ret & ~dev->data_mask) | bit)); |
|
matrox_w1_write_reg(dev, MATROX_GET_DATA, 0x00); |
|
} |
|
|
|
static u8 matrox_w1_read_ddc_bit(void *data) |
|
{ |
|
u8 ret; |
|
struct matrox_device *dev = data; |
|
|
|
ret = matrox_w1_read_reg(dev, MATROX_GET_DATA); |
|
|
|
return ret; |
|
} |
|
|
|
static void matrox_w1_hw_init(struct matrox_device *dev) |
|
{ |
|
matrox_w1_write_reg(dev, MATROX_GET_DATA, 0xFF); |
|
matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00); |
|
} |
|
|
|
static int matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
|
{ |
|
struct matrox_device *dev; |
|
int err; |
|
|
|
if (pdev->vendor != PCI_VENDOR_ID_MATROX || pdev->device != PCI_DEVICE_ID_MATROX_G400) |
|
return -ENODEV; |
|
|
|
dev = kzalloc(sizeof(struct matrox_device) + |
|
sizeof(struct w1_bus_master), GFP_KERNEL); |
|
if (!dev) { |
|
dev_err(&pdev->dev, |
|
"%s: Failed to create new matrox_device object.\n", |
|
__func__); |
|
return -ENOMEM; |
|
} |
|
|
|
|
|
dev->bus_master = (struct w1_bus_master *)(dev + 1); |
|
|
|
/* |
|
* True for G400, for some other we need resource 0, see drivers/video/matrox/matroxfb_base.c |
|
*/ |
|
|
|
dev->phys_addr = pci_resource_start(pdev, 1); |
|
|
|
dev->virt_addr = ioremap(dev->phys_addr, 16384); |
|
if (!dev->virt_addr) { |
|
dev_err(&pdev->dev, "%s: failed to ioremap(0x%lx, %d).\n", |
|
__func__, dev->phys_addr, 16384); |
|
err = -EIO; |
|
goto err_out_free_device; |
|
} |
|
|
|
dev->base_addr = dev->virt_addr + MATROX_BASE; |
|
dev->port_index = dev->base_addr + MATROX_PORT_INDEX_OFFSET; |
|
dev->port_data = dev->base_addr + MATROX_PORT_DATA_OFFSET; |
|
dev->data_mask = (MATROX_G400_DDC_DATA); |
|
|
|
matrox_w1_hw_init(dev); |
|
|
|
dev->bus_master->data = dev; |
|
dev->bus_master->read_bit = &matrox_w1_read_ddc_bit; |
|
dev->bus_master->write_bit = &matrox_w1_write_ddc_bit; |
|
|
|
err = w1_add_master_device(dev->bus_master); |
|
if (err) |
|
goto err_out_free_device; |
|
|
|
pci_set_drvdata(pdev, dev); |
|
|
|
dev->found = 1; |
|
|
|
dev_info(&pdev->dev, "Matrox G400 GPIO transport layer for 1-wire.\n"); |
|
|
|
return 0; |
|
|
|
err_out_free_device: |
|
if (dev->virt_addr) |
|
iounmap(dev->virt_addr); |
|
kfree(dev); |
|
|
|
return err; |
|
} |
|
|
|
static void matrox_w1_remove(struct pci_dev *pdev) |
|
{ |
|
struct matrox_device *dev = pci_get_drvdata(pdev); |
|
|
|
if (dev->found) { |
|
w1_remove_master_device(dev->bus_master); |
|
iounmap(dev->virt_addr); |
|
} |
|
kfree(dev); |
|
} |
|
|
|
static struct pci_device_id matrox_w1_tbl[] = { |
|
{ PCI_DEVICE(PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400) }, |
|
{ }, |
|
}; |
|
MODULE_DEVICE_TABLE(pci, matrox_w1_tbl); |
|
|
|
static struct pci_driver matrox_w1_pci_driver = { |
|
.name = "matrox_w1", |
|
.id_table = matrox_w1_tbl, |
|
.probe = matrox_w1_probe, |
|
.remove = matrox_w1_remove, |
|
}; |
|
module_pci_driver(matrox_w1_pci_driver); |
|
|
|
MODULE_AUTHOR("Evgeniy Polyakov <[email protected]>"); |
|
MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire protocol) over VGA DDC(matrox gpio)."); |
|
MODULE_LICENSE("GPL");
|
|
|