18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	matrox_w1.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <asm/types.h>
98c2ecf20Sopenharmony_ci#include <linux/atomic.h>
108c2ecf20Sopenharmony_ci#include <asm/io.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/list.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
188c2ecf20Sopenharmony_ci#include <linux/timer.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
218c2ecf20Sopenharmony_ci#include <linux/pci.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/w1.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Matrox G400 DDC registers.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define MATROX_G400_DDC_CLK		(1<<4)
308c2ecf20Sopenharmony_ci#define MATROX_G400_DDC_DATA		(1<<1)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define MATROX_BASE			0x3C00
338c2ecf20Sopenharmony_ci#define MATROX_STATUS			0x1e14
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define MATROX_PORT_INDEX_OFFSET	0x00
368c2ecf20Sopenharmony_ci#define MATROX_PORT_DATA_OFFSET		0x0A
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define MATROX_GET_CONTROL		0x2A
398c2ecf20Sopenharmony_ci#define MATROX_GET_DATA			0x2B
408c2ecf20Sopenharmony_ci#define MATROX_CURSOR_CTL		0x06
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct matrox_device
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	void __iomem *base_addr;
458c2ecf20Sopenharmony_ci	void __iomem *port_index;
468c2ecf20Sopenharmony_ci	void __iomem *port_data;
478c2ecf20Sopenharmony_ci	u8 data_mask;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	unsigned long phys_addr;
508c2ecf20Sopenharmony_ci	void __iomem *virt_addr;
518c2ecf20Sopenharmony_ci	unsigned long found;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	struct w1_bus_master *bus_master;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * These functions read and write DDC Data bit.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * Using tristate pins, since i can't find any open-drain pin in whole motherboard.
608c2ecf20Sopenharmony_ci * Unfortunately we can't connect to Intel's 82801xx IO controller
618c2ecf20Sopenharmony_ci * since we don't know motherboard schema, which has pretty unused(may be not) GPIO.
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * I've heard that PIIX also has open drain pin.
648c2ecf20Sopenharmony_ci *
658c2ecf20Sopenharmony_ci * Port mapping.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistatic __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	u8 ret;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	writeb(reg, dev->port_index);
728c2ecf20Sopenharmony_ci	ret = readb(dev->port_data);
738c2ecf20Sopenharmony_ci	barrier();
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return ret;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic __inline__ void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	writeb(reg, dev->port_index);
818c2ecf20Sopenharmony_ci	writeb(val, dev->port_data);
828c2ecf20Sopenharmony_ci	wmb();
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void matrox_w1_write_ddc_bit(void *data, u8 bit)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	u8 ret;
888c2ecf20Sopenharmony_ci	struct matrox_device *dev = data;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (bit)
918c2ecf20Sopenharmony_ci		bit = 0;
928c2ecf20Sopenharmony_ci	else
938c2ecf20Sopenharmony_ci		bit = dev->data_mask;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	ret = matrox_w1_read_reg(dev, MATROX_GET_CONTROL);
968c2ecf20Sopenharmony_ci	matrox_w1_write_reg(dev, MATROX_GET_CONTROL, ((ret & ~dev->data_mask) | bit));
978c2ecf20Sopenharmony_ci	matrox_w1_write_reg(dev, MATROX_GET_DATA, 0x00);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic u8 matrox_w1_read_ddc_bit(void *data)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	u8 ret;
1038c2ecf20Sopenharmony_ci	struct matrox_device *dev = data;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	ret = matrox_w1_read_reg(dev, MATROX_GET_DATA);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return ret;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic void matrox_w1_hw_init(struct matrox_device *dev)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	matrox_w1_write_reg(dev, MATROX_GET_DATA, 0xFF);
1138c2ecf20Sopenharmony_ci	matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct matrox_device *dev;
1198c2ecf20Sopenharmony_ci	int err;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (pdev->vendor != PCI_VENDOR_ID_MATROX || pdev->device != PCI_DEVICE_ID_MATROX_G400)
1228c2ecf20Sopenharmony_ci		return -ENODEV;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(struct matrox_device) +
1258c2ecf20Sopenharmony_ci		       sizeof(struct w1_bus_master), GFP_KERNEL);
1268c2ecf20Sopenharmony_ci	if (!dev) {
1278c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
1288c2ecf20Sopenharmony_ci			"%s: Failed to create new matrox_device object.\n",
1298c2ecf20Sopenharmony_ci			__func__);
1308c2ecf20Sopenharmony_ci		return -ENOMEM;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	dev->bus_master = (struct w1_bus_master *)(dev + 1);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * True for G400, for some other we need resource 0, see drivers/video/matrox/matroxfb_base.c
1388c2ecf20Sopenharmony_ci	 */
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	dev->phys_addr = pci_resource_start(pdev, 1);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	dev->virt_addr = ioremap(dev->phys_addr, 16384);
1438c2ecf20Sopenharmony_ci	if (!dev->virt_addr) {
1448c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "%s: failed to ioremap(0x%lx, %d).\n",
1458c2ecf20Sopenharmony_ci			__func__, dev->phys_addr, 16384);
1468c2ecf20Sopenharmony_ci		err = -EIO;
1478c2ecf20Sopenharmony_ci		goto err_out_free_device;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	dev->base_addr = dev->virt_addr + MATROX_BASE;
1518c2ecf20Sopenharmony_ci	dev->port_index = dev->base_addr + MATROX_PORT_INDEX_OFFSET;
1528c2ecf20Sopenharmony_ci	dev->port_data = dev->base_addr + MATROX_PORT_DATA_OFFSET;
1538c2ecf20Sopenharmony_ci	dev->data_mask = (MATROX_G400_DDC_DATA);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	matrox_w1_hw_init(dev);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	dev->bus_master->data = dev;
1588c2ecf20Sopenharmony_ci	dev->bus_master->read_bit = &matrox_w1_read_ddc_bit;
1598c2ecf20Sopenharmony_ci	dev->bus_master->write_bit = &matrox_w1_write_ddc_bit;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	err = w1_add_master_device(dev->bus_master);
1628c2ecf20Sopenharmony_ci	if (err)
1638c2ecf20Sopenharmony_ci		goto err_out_free_device;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, dev);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	dev->found = 1;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "Matrox G400 GPIO transport layer for 1-wire.\n");
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return 0;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cierr_out_free_device:
1748c2ecf20Sopenharmony_ci	if (dev->virt_addr)
1758c2ecf20Sopenharmony_ci		iounmap(dev->virt_addr);
1768c2ecf20Sopenharmony_ci	kfree(dev);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return err;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void matrox_w1_remove(struct pci_dev *pdev)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct matrox_device *dev = pci_get_drvdata(pdev);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (dev->found) {
1868c2ecf20Sopenharmony_ci		w1_remove_master_device(dev->bus_master);
1878c2ecf20Sopenharmony_ci		iounmap(dev->virt_addr);
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci	kfree(dev);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic struct pci_device_id matrox_w1_tbl[] = {
1938c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400) },
1948c2ecf20Sopenharmony_ci	{ },
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, matrox_w1_tbl);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic struct pci_driver matrox_w1_pci_driver = {
1998c2ecf20Sopenharmony_ci	.name = "matrox_w1",
2008c2ecf20Sopenharmony_ci	.id_table = matrox_w1_tbl,
2018c2ecf20Sopenharmony_ci	.probe = matrox_w1_probe,
2028c2ecf20Sopenharmony_ci	.remove = matrox_w1_remove,
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_cimodule_pci_driver(matrox_w1_pci_driver);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
2078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for transport(Dallas 1-wire protocol) over VGA DDC(matrox gpio).");
2088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
209