18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Goramo PCI200SYN synchronous serial card driver for Linux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2002-2008 Krzysztof Halasa <khc@pm.waw.pl>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * For information see <https://www.kernel.org/pub/linux/utils/net/hdlc/>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Sources of information:
108c2ecf20Sopenharmony_ci *    Hitachi HD64572 SCA-II User's Manual
118c2ecf20Sopenharmony_ci *    PLX Technology Inc. PCI9052 Data Book
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/capability.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/types.h>
218c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
228c2ecf20Sopenharmony_ci#include <linux/in.h>
238c2ecf20Sopenharmony_ci#include <linux/string.h>
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci#include <linux/init.h>
268c2ecf20Sopenharmony_ci#include <linux/ioport.h>
278c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
288c2ecf20Sopenharmony_ci#include <linux/hdlc.h>
298c2ecf20Sopenharmony_ci#include <linux/pci.h>
308c2ecf20Sopenharmony_ci#include <linux/delay.h>
318c2ecf20Sopenharmony_ci#include <asm/io.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "hd64572.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#undef DEBUG_PKT
368c2ecf20Sopenharmony_ci#define DEBUG_RINGS
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define PCI200SYN_PLX_SIZE	0x80	/* PLX control window size (128b) */
398c2ecf20Sopenharmony_ci#define PCI200SYN_SCA_SIZE	0x400	/* SCA window size (1Kb) */
408c2ecf20Sopenharmony_ci#define MAX_TX_BUFFERS		10
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int pci_clock_freq = 33000000;
438c2ecf20Sopenharmony_ci#define CLOCK_BASE pci_clock_freq
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci *      PLX PCI9052 local configuration and shared runtime registers.
478c2ecf20Sopenharmony_ci *      This structure can be used to access 9052 registers (memory mapped).
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_citypedef struct {
508c2ecf20Sopenharmony_ci	u32 loc_addr_range[4];	/* 00-0Ch : Local Address Ranges */
518c2ecf20Sopenharmony_ci	u32 loc_rom_range;	/* 10h : Local ROM Range */
528c2ecf20Sopenharmony_ci	u32 loc_addr_base[4];	/* 14-20h : Local Address Base Addrs */
538c2ecf20Sopenharmony_ci	u32 loc_rom_base;	/* 24h : Local ROM Base */
548c2ecf20Sopenharmony_ci	u32 loc_bus_descr[4];	/* 28-34h : Local Bus Descriptors */
558c2ecf20Sopenharmony_ci	u32 rom_bus_descr;	/* 38h : ROM Bus Descriptor */
568c2ecf20Sopenharmony_ci	u32 cs_base[4];		/* 3C-48h : Chip Select Base Addrs */
578c2ecf20Sopenharmony_ci	u32 intr_ctrl_stat;	/* 4Ch : Interrupt Control/Status */
588c2ecf20Sopenharmony_ci	u32 init_ctrl;		/* 50h : EEPROM ctrl, Init Ctrl, etc */
598c2ecf20Sopenharmony_ci}plx9052;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_citypedef struct port_s {
648c2ecf20Sopenharmony_ci	struct napi_struct napi;
658c2ecf20Sopenharmony_ci	struct net_device *netdev;
668c2ecf20Sopenharmony_ci	struct card_s *card;
678c2ecf20Sopenharmony_ci	spinlock_t lock;	/* TX lock */
688c2ecf20Sopenharmony_ci	sync_serial_settings settings;
698c2ecf20Sopenharmony_ci	int rxpart;		/* partial frame received, next frame invalid*/
708c2ecf20Sopenharmony_ci	unsigned short encoding;
718c2ecf20Sopenharmony_ci	unsigned short parity;
728c2ecf20Sopenharmony_ci	u16 rxin;		/* rx ring buffer 'in' pointer */
738c2ecf20Sopenharmony_ci	u16 txin;		/* tx ring buffer 'in' and 'last' pointers */
748c2ecf20Sopenharmony_ci	u16 txlast;
758c2ecf20Sopenharmony_ci	u8 rxs, txs, tmc;	/* SCA registers */
768c2ecf20Sopenharmony_ci	u8 chan;		/* physical port # - 0 or 1 */
778c2ecf20Sopenharmony_ci}port_t;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_citypedef struct card_s {
828c2ecf20Sopenharmony_ci	u8 __iomem *rambase;	/* buffer memory base (virtual) */
838c2ecf20Sopenharmony_ci	u8 __iomem *scabase;	/* SCA memory base (virtual) */
848c2ecf20Sopenharmony_ci	plx9052 __iomem *plxbase;/* PLX registers memory base (virtual) */
858c2ecf20Sopenharmony_ci	u16 rx_ring_buffers;	/* number of buffers in a ring */
868c2ecf20Sopenharmony_ci	u16 tx_ring_buffers;
878c2ecf20Sopenharmony_ci	u16 buff_offset;	/* offset of first buffer of first channel */
888c2ecf20Sopenharmony_ci	u8 irq;			/* interrupt request level */
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	port_t ports[2];
918c2ecf20Sopenharmony_ci}card_t;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define get_port(card, port)	     (&card->ports[port])
958c2ecf20Sopenharmony_ci#define sca_flush(card)		     (sca_in(IER0, card));
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic inline void new_memcpy_toio(char __iomem *dest, char *src, int length)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int len;
1008c2ecf20Sopenharmony_ci	do {
1018c2ecf20Sopenharmony_ci		len = length > 256 ? 256 : length;
1028c2ecf20Sopenharmony_ci		memcpy_toio(dest, src, len);
1038c2ecf20Sopenharmony_ci		dest += len;
1048c2ecf20Sopenharmony_ci		src += len;
1058c2ecf20Sopenharmony_ci		length -= len;
1068c2ecf20Sopenharmony_ci		readb(dest);
1078c2ecf20Sopenharmony_ci	} while (len);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#undef memcpy_toio
1118c2ecf20Sopenharmony_ci#define memcpy_toio new_memcpy_toio
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#include "hd64572.c"
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void pci200_set_iface(port_t *port)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	card_t *card = port->card;
1198c2ecf20Sopenharmony_ci	u16 msci = get_msci(port);
1208c2ecf20Sopenharmony_ci	u8 rxs = port->rxs & CLK_BRG_MASK;
1218c2ecf20Sopenharmony_ci	u8 txs = port->txs & CLK_BRG_MASK;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	sca_out(EXS_TES1, (port->chan ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
1248c2ecf20Sopenharmony_ci		port->card);
1258c2ecf20Sopenharmony_ci	switch(port->settings.clock_type) {
1268c2ecf20Sopenharmony_ci	case CLOCK_INT:
1278c2ecf20Sopenharmony_ci		rxs |= CLK_BRG; /* BRG output */
1288c2ecf20Sopenharmony_ci		txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	case CLOCK_TXINT:
1328c2ecf20Sopenharmony_ci		rxs |= CLK_LINE; /* RXC input */
1338c2ecf20Sopenharmony_ci		txs |= CLK_PIN_OUT | CLK_BRG; /* BRG output */
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	case CLOCK_TXFROMRX:
1378c2ecf20Sopenharmony_ci		rxs |= CLK_LINE; /* RXC input */
1388c2ecf20Sopenharmony_ci		txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	default:		/* EXTernal clock */
1428c2ecf20Sopenharmony_ci		rxs |= CLK_LINE; /* RXC input */
1438c2ecf20Sopenharmony_ci		txs |= CLK_PIN_OUT | CLK_LINE; /* TXC input */
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	port->rxs = rxs;
1488c2ecf20Sopenharmony_ci	port->txs = txs;
1498c2ecf20Sopenharmony_ci	sca_out(rxs, msci + RXS, card);
1508c2ecf20Sopenharmony_ci	sca_out(txs, msci + TXS, card);
1518c2ecf20Sopenharmony_ci	sca_set_port(port);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int pci200_open(struct net_device *dev)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	port_t *port = dev_to_port(dev);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	int result = hdlc_open(dev);
1618c2ecf20Sopenharmony_ci	if (result)
1628c2ecf20Sopenharmony_ci		return result;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	sca_open(dev);
1658c2ecf20Sopenharmony_ci	pci200_set_iface(port);
1668c2ecf20Sopenharmony_ci	sca_flush(port->card);
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int pci200_close(struct net_device *dev)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	sca_close(dev);
1758c2ecf20Sopenharmony_ci	sca_flush(dev_to_port(dev)->card);
1768c2ecf20Sopenharmony_ci	hdlc_close(dev);
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int pci200_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	const size_t size = sizeof(sync_serial_settings);
1858c2ecf20Sopenharmony_ci	sync_serial_settings new_line;
1868c2ecf20Sopenharmony_ci	sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
1878c2ecf20Sopenharmony_ci	port_t *port = dev_to_port(dev);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci#ifdef DEBUG_RINGS
1908c2ecf20Sopenharmony_ci	if (cmd == SIOCDEVPRIVATE) {
1918c2ecf20Sopenharmony_ci		sca_dump_rings(dev);
1928c2ecf20Sopenharmony_ci		return 0;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci#endif
1958c2ecf20Sopenharmony_ci	if (cmd != SIOCWANDEV)
1968c2ecf20Sopenharmony_ci		return hdlc_ioctl(dev, ifr, cmd);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	switch(ifr->ifr_settings.type) {
1998c2ecf20Sopenharmony_ci	case IF_GET_IFACE:
2008c2ecf20Sopenharmony_ci		ifr->ifr_settings.type = IF_IFACE_V35;
2018c2ecf20Sopenharmony_ci		if (ifr->ifr_settings.size < size) {
2028c2ecf20Sopenharmony_ci			ifr->ifr_settings.size = size; /* data size wanted */
2038c2ecf20Sopenharmony_ci			return -ENOBUFS;
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci		if (copy_to_user(line, &port->settings, size))
2068c2ecf20Sopenharmony_ci			return -EFAULT;
2078c2ecf20Sopenharmony_ci		return 0;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	case IF_IFACE_V35:
2108c2ecf20Sopenharmony_ci	case IF_IFACE_SYNC_SERIAL:
2118c2ecf20Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
2128c2ecf20Sopenharmony_ci			return -EPERM;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		if (copy_from_user(&new_line, line, size))
2158c2ecf20Sopenharmony_ci			return -EFAULT;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		if (new_line.clock_type != CLOCK_EXT &&
2188c2ecf20Sopenharmony_ci		    new_line.clock_type != CLOCK_TXFROMRX &&
2198c2ecf20Sopenharmony_ci		    new_line.clock_type != CLOCK_INT &&
2208c2ecf20Sopenharmony_ci		    new_line.clock_type != CLOCK_TXINT)
2218c2ecf20Sopenharmony_ci			return -EINVAL;	/* No such clock setting */
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		if (new_line.loopback != 0 && new_line.loopback != 1)
2248c2ecf20Sopenharmony_ci			return -EINVAL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		memcpy(&port->settings, &new_line, size); /* Update settings */
2278c2ecf20Sopenharmony_ci		pci200_set_iface(port);
2288c2ecf20Sopenharmony_ci		sca_flush(port->card);
2298c2ecf20Sopenharmony_ci		return 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	default:
2328c2ecf20Sopenharmony_ci		return hdlc_ioctl(dev, ifr, cmd);
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic void pci200_pci_remove_one(struct pci_dev *pdev)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	int i;
2418c2ecf20Sopenharmony_ci	card_t *card = pci_get_drvdata(pdev);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++)
2448c2ecf20Sopenharmony_ci		if (card->ports[i].card)
2458c2ecf20Sopenharmony_ci			unregister_hdlc_device(card->ports[i].netdev);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (card->irq)
2488c2ecf20Sopenharmony_ci		free_irq(card->irq, card);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (card->rambase)
2518c2ecf20Sopenharmony_ci		iounmap(card->rambase);
2528c2ecf20Sopenharmony_ci	if (card->scabase)
2538c2ecf20Sopenharmony_ci		iounmap(card->scabase);
2548c2ecf20Sopenharmony_ci	if (card->plxbase)
2558c2ecf20Sopenharmony_ci		iounmap(card->plxbase);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
2588c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
2598c2ecf20Sopenharmony_ci	if (card->ports[0].netdev)
2608c2ecf20Sopenharmony_ci		free_netdev(card->ports[0].netdev);
2618c2ecf20Sopenharmony_ci	if (card->ports[1].netdev)
2628c2ecf20Sopenharmony_ci		free_netdev(card->ports[1].netdev);
2638c2ecf20Sopenharmony_ci	kfree(card);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic const struct net_device_ops pci200_ops = {
2678c2ecf20Sopenharmony_ci	.ndo_open       = pci200_open,
2688c2ecf20Sopenharmony_ci	.ndo_stop       = pci200_close,
2698c2ecf20Sopenharmony_ci	.ndo_start_xmit = hdlc_start_xmit,
2708c2ecf20Sopenharmony_ci	.ndo_do_ioctl   = pci200_ioctl,
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int pci200_pci_init_one(struct pci_dev *pdev,
2748c2ecf20Sopenharmony_ci			       const struct pci_device_id *ent)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	card_t *card;
2778c2ecf20Sopenharmony_ci	u32 __iomem *p;
2788c2ecf20Sopenharmony_ci	int i;
2798c2ecf20Sopenharmony_ci	u32 ramsize;
2808c2ecf20Sopenharmony_ci	u32 ramphys;		/* buffer memory base */
2818c2ecf20Sopenharmony_ci	u32 scaphys;		/* SCA memory base */
2828c2ecf20Sopenharmony_ci	u32 plxphys;		/* PLX registers memory base */
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	i = pci_enable_device(pdev);
2858c2ecf20Sopenharmony_ci	if (i)
2868c2ecf20Sopenharmony_ci		return i;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	i = pci_request_regions(pdev, "PCI200SYN");
2898c2ecf20Sopenharmony_ci	if (i) {
2908c2ecf20Sopenharmony_ci		pci_disable_device(pdev);
2918c2ecf20Sopenharmony_ci		return i;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(card_t), GFP_KERNEL);
2958c2ecf20Sopenharmony_ci	if (card == NULL) {
2968c2ecf20Sopenharmony_ci		pci_release_regions(pdev);
2978c2ecf20Sopenharmony_ci		pci_disable_device(pdev);
2988c2ecf20Sopenharmony_ci		return -ENOBUFS;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, card);
3018c2ecf20Sopenharmony_ci	card->ports[0].netdev = alloc_hdlcdev(&card->ports[0]);
3028c2ecf20Sopenharmony_ci	card->ports[1].netdev = alloc_hdlcdev(&card->ports[1]);
3038c2ecf20Sopenharmony_ci	if (!card->ports[0].netdev || !card->ports[1].netdev) {
3048c2ecf20Sopenharmony_ci		pr_err("unable to allocate memory\n");
3058c2ecf20Sopenharmony_ci		pci200_pci_remove_one(pdev);
3068c2ecf20Sopenharmony_ci		return -ENOMEM;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (pci_resource_len(pdev, 0) != PCI200SYN_PLX_SIZE ||
3108c2ecf20Sopenharmony_ci	    pci_resource_len(pdev, 2) != PCI200SYN_SCA_SIZE ||
3118c2ecf20Sopenharmony_ci	    pci_resource_len(pdev, 3) < 16384) {
3128c2ecf20Sopenharmony_ci		pr_err("invalid card EEPROM parameters\n");
3138c2ecf20Sopenharmony_ci		pci200_pci_remove_one(pdev);
3148c2ecf20Sopenharmony_ci		return -EFAULT;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	plxphys = pci_resource_start(pdev,0) & PCI_BASE_ADDRESS_MEM_MASK;
3188c2ecf20Sopenharmony_ci	card->plxbase = ioremap(plxphys, PCI200SYN_PLX_SIZE);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	scaphys = pci_resource_start(pdev,2) & PCI_BASE_ADDRESS_MEM_MASK;
3218c2ecf20Sopenharmony_ci	card->scabase = ioremap(scaphys, PCI200SYN_SCA_SIZE);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK;
3248c2ecf20Sopenharmony_ci	card->rambase = pci_ioremap_bar(pdev, 3);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (card->plxbase == NULL ||
3278c2ecf20Sopenharmony_ci	    card->scabase == NULL ||
3288c2ecf20Sopenharmony_ci	    card->rambase == NULL) {
3298c2ecf20Sopenharmony_ci		pr_err("ioremap() failed\n");
3308c2ecf20Sopenharmony_ci		pci200_pci_remove_one(pdev);
3318c2ecf20Sopenharmony_ci		return -EFAULT;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* Reset PLX */
3358c2ecf20Sopenharmony_ci	p = &card->plxbase->init_ctrl;
3368c2ecf20Sopenharmony_ci	writel(readl(p) | 0x40000000, p);
3378c2ecf20Sopenharmony_ci	readl(p);		/* Flush the write - do not use sca_flush */
3388c2ecf20Sopenharmony_ci	udelay(1);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	writel(readl(p) & ~0x40000000, p);
3418c2ecf20Sopenharmony_ci	readl(p);		/* Flush the write - do not use sca_flush */
3428c2ecf20Sopenharmony_ci	udelay(1);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	ramsize = sca_detect_ram(card, card->rambase,
3458c2ecf20Sopenharmony_ci				 pci_resource_len(pdev, 3));
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* number of TX + RX buffers for one port - this is dual port card */
3488c2ecf20Sopenharmony_ci	i = ramsize / (2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
3498c2ecf20Sopenharmony_ci	card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
3508c2ecf20Sopenharmony_ci	card->rx_ring_buffers = i - card->tx_ring_buffers;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	card->buff_offset = 2 * sizeof(pkt_desc) * (card->tx_ring_buffers +
3538c2ecf20Sopenharmony_ci						    card->rx_ring_buffers);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	pr_info("%u KB RAM at 0x%x, IRQ%u, using %u TX + %u RX packets rings\n",
3568c2ecf20Sopenharmony_ci		ramsize / 1024, ramphys,
3578c2ecf20Sopenharmony_ci		pdev->irq, card->tx_ring_buffers, card->rx_ring_buffers);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (card->tx_ring_buffers < 1) {
3608c2ecf20Sopenharmony_ci		pr_err("RAM test failed\n");
3618c2ecf20Sopenharmony_ci		pci200_pci_remove_one(pdev);
3628c2ecf20Sopenharmony_ci		return -EFAULT;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* Enable interrupts on the PCI bridge */
3668c2ecf20Sopenharmony_ci	p = &card->plxbase->intr_ctrl_stat;
3678c2ecf20Sopenharmony_ci	writew(readw(p) | 0x0040, p);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* Allocate IRQ */
3708c2ecf20Sopenharmony_ci	if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, "pci200syn", card)) {
3718c2ecf20Sopenharmony_ci		pr_warn("could not allocate IRQ%d\n", pdev->irq);
3728c2ecf20Sopenharmony_ci		pci200_pci_remove_one(pdev);
3738c2ecf20Sopenharmony_ci		return -EBUSY;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci	card->irq = pdev->irq;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	sca_init(card, 0);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
3808c2ecf20Sopenharmony_ci		port_t *port = &card->ports[i];
3818c2ecf20Sopenharmony_ci		struct net_device *dev = port->netdev;
3828c2ecf20Sopenharmony_ci		hdlc_device *hdlc = dev_to_hdlc(dev);
3838c2ecf20Sopenharmony_ci		port->chan = i;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		spin_lock_init(&port->lock);
3868c2ecf20Sopenharmony_ci		dev->irq = card->irq;
3878c2ecf20Sopenharmony_ci		dev->mem_start = ramphys;
3888c2ecf20Sopenharmony_ci		dev->mem_end = ramphys + ramsize - 1;
3898c2ecf20Sopenharmony_ci		dev->tx_queue_len = 50;
3908c2ecf20Sopenharmony_ci		dev->netdev_ops = &pci200_ops;
3918c2ecf20Sopenharmony_ci		hdlc->attach = sca_attach;
3928c2ecf20Sopenharmony_ci		hdlc->xmit = sca_xmit;
3938c2ecf20Sopenharmony_ci		port->settings.clock_type = CLOCK_EXT;
3948c2ecf20Sopenharmony_ci		port->card = card;
3958c2ecf20Sopenharmony_ci		sca_init_port(port);
3968c2ecf20Sopenharmony_ci		if (register_hdlc_device(dev)) {
3978c2ecf20Sopenharmony_ci			pr_err("unable to register hdlc device\n");
3988c2ecf20Sopenharmony_ci			port->card = NULL;
3998c2ecf20Sopenharmony_ci			pci200_pci_remove_one(pdev);
4008c2ecf20Sopenharmony_ci			return -ENOBUFS;
4018c2ecf20Sopenharmony_ci		}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		netdev_info(dev, "PCI200SYN channel %d\n", port->chan);
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	sca_flush(card);
4078c2ecf20Sopenharmony_ci	return 0;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic const struct pci_device_id pci200_pci_tbl[] = {
4138c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX,
4148c2ecf20Sopenharmony_ci	  PCI_DEVICE_ID_PLX_PCI200SYN, 0, 0, 0 },
4158c2ecf20Sopenharmony_ci	{ 0, }
4168c2ecf20Sopenharmony_ci};
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic struct pci_driver pci200_pci_driver = {
4208c2ecf20Sopenharmony_ci	.name		= "PCI200SYN",
4218c2ecf20Sopenharmony_ci	.id_table	= pci200_pci_tbl,
4228c2ecf20Sopenharmony_ci	.probe		= pci200_pci_init_one,
4238c2ecf20Sopenharmony_ci	.remove		= pci200_pci_remove_one,
4248c2ecf20Sopenharmony_ci};
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int __init pci200_init_module(void)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
4308c2ecf20Sopenharmony_ci		pr_err("Invalid PCI clock frequency\n");
4318c2ecf20Sopenharmony_ci		return -EINVAL;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	return pci_register_driver(&pci200_pci_driver);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void __exit pci200_cleanup_module(void)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	pci_unregister_driver(&pci200_pci_driver);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
4448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Goramo PCI200SYN serial port driver");
4458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci200_pci_tbl);
4478c2ecf20Sopenharmony_cimodule_param(pci_clock_freq, int, 0444);
4488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pci_clock_freq, "System PCI clock frequency in Hz");
4498c2ecf20Sopenharmony_cimodule_init(pci200_init_module);
4508c2ecf20Sopenharmony_cimodule_exit(pci200_cleanup_module);
451