From 9d49da8a62648856f63e3b790df38f9eb39630ba Mon Sep 17 00:00:00 2001 From: Michael Hunold Date: Tue, 7 Dec 2004 00:22:44 -0800 Subject: [PATCH] dvb: core changes - [DVB] dvb-core: follow Linux coding style, kill dvb_ksyms.c and move the EXPORT_SYMBOLs to the files where the functions are, thanks to Adrian Bunk - [DVB] dvb-core: #if 0'ing unused code, make needlessly global code static, whitespace and newline cleanups, thanks to Adrian Bunk - [DVB] dvb_ca_en50221.c: support for KNC1/Cinergy CI modules, fix segfaults, enhanced poll_slot_status to support non-IRQ interfaces, Fix module usage count problem - [DVB] dvb-frontend.c: core changes to support the refactorized frontend drivers Signed-off-by: Michael Hunold Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/dvb/frontend.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index d0300dcff0af..585560022ba0 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -78,7 +78,7 @@ struct dvb_frontend_info { __u32 symbol_rate_min; __u32 symbol_rate_max; __u32 symbol_rate_tolerance; /* ppm */ - __u32 notifier_delay; /* ms */ + __u32 notifier_delay; /* DEPRECATED */ fe_caps_t caps; }; -- cgit v1.2.3 From 046de9be7179b9b97eb899aa7c5b0c2c83a87b21 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 7 Dec 2004 08:54:34 -0500 Subject: [libata] only DMA map data for DMA commands (fix >=4GB bug) libata made the assumption that (for PIO commands in this case) it could modify DMA memory at the kernel-virtual address, after mapping this. This is incorrect, and fails on e.g. platforms that copy DMA memory back and forth (swiotlb on Intel EM64T and IA64). Remove this assumption by ensuring that we only call the DMA mapping routines if we really are going to use DMA for data xfer. Also: remove a bogus WARN_ON() in ata_sg_init_one() which caused bug reports (but no problems). --- drivers/scsi/ahci.c | 3 ++- drivers/scsi/libata-core.c | 42 ++++++++++++++++++++++++++++++++++-------- include/linux/libata.h | 1 + 3 files changed, 37 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 8c300e0a52d9..e0cd4ba41678 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -229,7 +229,8 @@ static struct ata_port_info ahci_port_info[] = { { .sht = &ahci_sht, .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO, + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA, .pio_mask = 0x03, /* pio3-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 1d2b344aab01..93d987433966 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1950,8 +1950,6 @@ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen) sg->page = virt_to_page(buf); sg->offset = (unsigned long) buf & ~PAGE_MASK; sg_dma_len(sg) = buflen; - - WARN_ON(buflen > PAGE_SIZE); } void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, @@ -2693,6 +2691,30 @@ void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) VPRINTK("EXIT\n"); } +static inline int ata_should_dma_map(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + case ATA_PROT_ATAPI_DMA: + return 1; + + case ATA_PROT_ATAPI: + case ATA_PROT_PIO: + case ATA_PROT_PIO_MULT: + if (ap->flags & ATA_FLAG_PIO_DMA) + return 1; + + /* fall through */ + + default: + return 0; + } + + /* never reached */ +} + /** * ata_qc_issue - issue taskfile to device * @qc: command to issue to device @@ -2713,12 +2735,16 @@ int ata_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_SG) { - if (ata_sg_setup(qc)) - goto err_out; - } else if (qc->flags & ATA_QCFLAG_SINGLE) { - if (ata_sg_setup_one(qc)) - goto err_out; + if (ata_should_dma_map(qc)) { + if (qc->flags & ATA_QCFLAG_SG) { + if (ata_sg_setup(qc)) + goto err_out; + } else if (qc->flags & ATA_QCFLAG_SINGLE) { + if (ata_sg_setup_one(qc)) + goto err_out; + } + } else { + qc->flags &= ~ATA_QCFLAG_DMAMAP; } ap->ops->qc_prep(qc); diff --git a/include/linux/libata.h b/include/linux/libata.h index 605e0a728c0e..95a7b0ddb096 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -112,6 +112,7 @@ enum { ATA_FLAG_SRST = (1 << 5), /* use ATA SRST, not E.D.D. */ ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */ ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */ + ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */ ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */ ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */ -- cgit v1.2.3 From 620512af09f33236b4ea04372816b761d48586d9 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 7 Dec 2004 20:49:21 -0800 Subject: [NET]: Fix CMSG validation checks wrt. signedness. Noticed by Georgi Guninski. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/socket.h | 4 ++++ net/core/scm.c | 4 +--- net/ipv4/ip_sockglue.c | 5 +---- net/ipv6/datagram.c | 4 +--- net/sctp/socket.c | 6 +----- 5 files changed, 8 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/socket.h b/include/linux/socket.h index b3aef7bf1380..4c7d11301abf 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -90,6 +90,10 @@ struct cmsghdr { (struct cmsghdr *)(ctl) : \ (struct cmsghdr *)NULL) #define CMSG_FIRSTHDR(msg) __CMSG_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) +#define CMSG_OK(mhdr, cmsg) ((cmsg)->cmsg_len >= sizeof(struct cmsghdr) && \ + (cmsg)->cmsg_len <= (unsigned long) \ + ((mhdr)->msg_controllen - \ + ((char *)(cmsg) - (char *)(mhdr)->msg_control))) /* * This mess will go away with glibc diff --git a/net/core/scm.c b/net/core/scm.c index 3699df388ebe..a2ebf30f6aa8 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -127,9 +127,7 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) for too short ancillary data object at all! Oops. OK, let's add it... */ - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) + if (!CMSG_OK(msg, cmsg)) goto error; if (cmsg->cmsg_level != SOL_SOCKET) diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 8bb874be141a..d352252326c1 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -146,11 +146,8 @@ int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc) struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) { + if (!CMSG_OK(msg, cmsg)) return -EINVAL; - } if (cmsg->cmsg_level != SOL_IP) continue; switch (cmsg->cmsg_type) { diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 770284e5e7c9..b077cd19f576 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -427,9 +427,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, int addr_type; struct net_device *dev = NULL; - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) { + if (!CMSG_OK(msg, cmsg)) { err = -EINVAL; goto exit_f; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2686c2dbabd2..d9967aaefecf 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4098,12 +4098,8 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg, for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR((struct msghdr*)msg, cmsg)) { - /* Check for minimum length. The SCM code has this check. */ - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) { + if (!CMSG_OK(msg, cmsg)) return -EINVAL; - } /* Should we parse this header or ignore? */ if (cmsg->cmsg_level != IPPROTO_SCTP) -- cgit v1.2.3 From d3e8ea419c137eff86517cefe086549600800449 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 8 Dec 2004 20:35:50 -0800 Subject: [PATCH] USB: sl811-hcd driver, replaces hc_sl811 This patch provides a new "sl811-hcd" driver, which should replace the older one from Cypress (which has been broken for ages, even on SA-1100). Key features of this new driver: - Small, relatively tight code; - Uses the 2.6 platform_device and usbcore HCD infrastructures; - Compiles (x86, ARM) and works (ARM/PXA255); - Passed a day's worth of "usbtest" stress testing (on 2.6.9). I've enumerated over a dozen different devices with it, and actually tested mice, hubs, keyboards, and usb-storage. There's a hardware erratum that prevents this chip from working with certain external hubs. There's scope yet for some performance work here; and some IRQ quirks linger. This PIO-only driver should serve as a model for some other non-DMA USB host controllers (like isp1161, isp1362, td243) used in embedded Linuxes ... in particular, showing how to maintain async and periodic schedules without pointless emulation of OHCI DMA queues and/or registers. The driver should handle ISO, but since it doesn't implement the special urb->iso_frame_desc[] "pseudo-queue" model (and since Linux can't guarantee low enough IRQ latencies!), ISO is disabled. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 16 +- drivers/usb/host/Makefile | 3 +- drivers/usb/host/sl811-hcd.c | 1905 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/sl811.h | 270 ++++++ include/linux/usb_sl811.h | 26 + 5 files changed, 2211 insertions(+), 9 deletions(-) create mode 100644 drivers/usb/host/sl811-hcd.c create mode 100644 drivers/usb/host/sl811.h create mode 100644 include/linux/usb_sl811.h (limited to 'include/linux') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f742bd95bd99..256787e33bde 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -100,14 +100,16 @@ config USB_UHCI_HCD To compile this driver as a module, choose M here: the module will be called uhci-hcd. -config USB_SL811HS - tristate "SL811HS support" - depends on ARM && USB +config USB_SL811_HCD + tristate "SL811HS HCD support" + depends on USB + default N help - Say Y here if you have a SL811HS USB host controller in your system. - - If you do not know what this is, please say N. + The SL811HS is a single-port USB controller that supports either + host side or peripheral side roles. Enable this option if your + board has this chip, and you want to use it as a host controller. + If unsure, say N. To compile this driver as a module, choose M here: the - module will be called hc_sl811. + module will be called sl811-hcd. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 15ee81a6340c..a574ca06cf6b 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -6,6 +6,5 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o - -obj-$(CONFIG_USB_SL811HS) += hc_sl811.o +obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c new file mode 100644 index 000000000000..7236c9981029 --- /dev/null +++ b/drivers/usb/host/sl811-hcd.c @@ -0,0 +1,1905 @@ +/* + * SL811HS HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Copyright (C) 2004 David Brownell + * + * Periodic scheduling is based on Roman's OHCI code + * Copyright (C) 1999 Roman Weissgaerber + * + * The SL811HS controller handles host side USB (like the SL11H, but with + * another register set and SOF generation) as well as peripheral side USB + * (like the SL811S). This driver version doesn't implement the Gadget API + * for the peripheral role; or OTG (that'd need much external circuitry). + * + * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" + * document (providing significant pieces missing from that spec); plus + * the SL811S spec if you want peripheral side info. + */ + +/* + * Status: Passed basic stress testing, works with hubs, mice, keyboards, + * and usb-storage. + * + * TODO: + * - usb suspend/resume triggered by sl811 (with USB_SUSPEND) + * - various issues noted in the code + * - performance work; use both register banks; ... + * - use urb->iso_frame_desc[] with ISO transfers + */ + +#undef VERBOSE +#undef PACKET_TRACE + +#include + +#ifdef CONFIG_USB_DEBUG +# define DEBUG +#else +# undef DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../core/hcd.h" +#include "sl811.h" + + +MODULE_DESCRIPTION("SL811HS USB Host Controller Driver"); +MODULE_LICENSE("GPL"); + +#define DRIVER_VERSION "06 Dec 2004" + + +#ifndef DEBUG +# define STUB_DEBUG_FILE +#endif + +/* for now, use only one transfer register bank */ +#undef USE_B + +/* this doesn't understand urb->iso_frame_desc[], but if you had a driver + * that just queued one ISO frame per URB then iso transfers "should" work + * using the normal urb status fields. + */ +#define DISABLE_ISO + +// #define QUIRK2 +#define QUIRK3 + +static const char hcd_name[] = "sl811-hcd"; + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs); + +static void port_power(struct sl811 *sl811, int is_on) +{ + /* hub is inactive unless the port is powered */ + if (is_on) { + if (sl811->port1 & (1 << USB_PORT_FEAT_POWER)) + return; + + sl811->port1 = (1 << USB_PORT_FEAT_POWER); + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811->hcd.self.controller->power.power_state = PM_SUSPEND_ON; + } else { + sl811->port1 = 0; + sl811->irq_enable = 0; + sl811->hcd.state = USB_STATE_HALT; + sl811->hcd.self.controller->power.power_state = PM_SUSPEND_DISK; + } + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, 0); + sl811_write(sl811, SL11H_IRQ_STATUS, ~0); + + if (sl811->board && sl811->board->port_power) { + /* switch VBUS, at 500mA unless hub power budget gets set */ + DBG("power %s\n", is_on ? "on" : "off"); + sl811->board->port_power(sl811->hcd.self.controller, is_on); + } + + /* reset as thoroughly as we can */ + if (sl811->board && sl811->board->reset) + sl811->board->reset(sl811->hcd.self.controller); + + sl811_write(sl811, SL11H_IRQ_ENABLE, 0); + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + // if !is_on, put into lowpower mode now +} + +/*-------------------------------------------------------------------------*/ + +/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, + * and may start I/O. Endpoint queues are scanned during completion irq + * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation. + * + * Using an external DMA engine to copy a packet at a time could work, + * though setup/teardown costs may be too big to make it worthwhile. + */ + +/* SETUP starts a new control request. Devices are not allowed to + * STALL or NAK these; they must cancel any pending control requests. + */ +static void setup_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + u8 addr; + u8 len; + void __iomem *data_reg; + + addr = SL811HS_PACKET_BUF(bank == 0); + len = sizeof(struct usb_ctrlrequest); + data_reg = sl811->data_reg; + sl811_write_buf(sl811, addr, urb->setup_packet, len); + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, data_reg); + writeb(SL_SETUP /* | ep->epnum */, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + /* always OUT/data0 */ ; + sl811_write(sl811, bank + SL11H_HOSTCTLREG, + control | SL11H_HCTLMASK_OUT); + ep->length = 0; + PACKET("SETUP qh%p\n", ep); +} + +/* STATUS finishes control requests, often after IN or OUT data packets */ +static void status_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + int do_out; + void __iomem *data_reg; + + do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe); + data_reg = sl811->data_reg; + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, 0); + writeb(0, data_reg); + writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + /* always data1; sometimes IN */ + control |= SL11H_HCTLMASK_TOGGLE; + if (do_out) + control |= SL11H_HCTLMASK_OUT; + sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); + ep->length = 0; + PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "", + do_out ? "out" : "in", ep); +} + +/* IN packets can be used with any type of endpoint. here we just + * start the transfer, data from the peripheral may arrive later. + * urb->iso_frame_desc is currently ignored here... + */ +static void in_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + u8 addr; + u8 len; + void __iomem *data_reg; + + /* avoid losing data on overflow */ + len = ep->maxpacket; + addr = SL811HS_PACKET_BUF(bank == 0); + if (!(control & SL11H_HCTLMASK_ISOCH) + && usb_gettoggle(urb->dev, ep->epnum, 0)) + control |= SL11H_HCTLMASK_TOGGLE; + data_reg = sl811->data_reg; + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, data_reg); + writeb(SL_IN | ep->epnum, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); + ep->length = min((int)len, + urb->transfer_buffer_length - urb->actual_length); + PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", + !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len); +} + +/* OUT packets can be used with any type of endpoint. + * urb->iso_frame_desc is currently ignored here... + */ +static void out_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + void *buf; + u8 addr; + u8 len; + void __iomem *data_reg; + + buf = urb->transfer_buffer + urb->actual_length; + prefetch(buf); + + len = min((int)ep->maxpacket, + urb->transfer_buffer_length - urb->actual_length); + + if (!(control & SL11H_HCTLMASK_ISOCH) + && usb_gettoggle(urb->dev, ep->epnum, 1)) + control |= SL11H_HCTLMASK_TOGGLE; + addr = SL811HS_PACKET_BUF(bank == 0); + data_reg = sl811->data_reg; + + sl811_write_buf(sl811, addr, buf, len); + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, data_reg); + writeb(SL_OUT | ep->epnum, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + sl811_write(sl811, bank + SL11H_HOSTCTLREG, + control | SL11H_HCTLMASK_OUT); + ep->length = len; + PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", + !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len); +} + +/*-------------------------------------------------------------------------*/ + +/* caller updates on-chip enables later */ + +static inline void sofirq_on(struct sl811 *sl811) +{ + if (sl811->irq_enable & SL11H_INTMASK_SOFINTR) + return; + VDBG("sof irq on\n"); + sl811->irq_enable |= SL11H_INTMASK_SOFINTR; +} + +static inline void sofirq_off(struct sl811 *sl811) +{ + if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR)) + return; + VDBG("sof irq off\n"); + sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR; +} + +/*-------------------------------------------------------------------------*/ + +/* pick the next endpoint for a transaction, and issue it. + * frames start with periodic transfers (after whatever is pending + * from the previous frame), and the rest of the time is async + * transfers, scheduled round-robin. + */ +static struct sl811h_ep *start(struct sl811 *sl811, u8 bank) +{ + struct sl811h_ep *ep; + struct sl811h_req *req; + struct urb *urb; + int fclock; + u8 control; + + /* use endpoint at schedule head */ + if (sl811->next_periodic) { + ep = sl811->next_periodic; + sl811->next_periodic = ep->next; + } else { + if (sl811->next_async) + ep = sl811->next_async; + else if (!list_empty(&sl811->async)) + ep = container_of(sl811->async.next, + struct sl811h_ep, schedule); + else { + /* could set up the first fullspeed periodic + * transfer for the next frame ... + */ + return NULL; + } + +#ifdef USE_B + if ((bank && sl811->active_b == ep) || sl811->active_a == ep) + return NULL; +#endif + + if (ep->schedule.next == &sl811->async) + sl811->next_async = NULL; + else + sl811->next_async = container_of(ep->schedule.next, + struct sl811h_ep, schedule); + } + + if (unlikely(list_empty(&ep->queue))) { + DBG("empty %p queue?\n", ep); + return NULL; + } + + req = container_of(ep->queue.next, struct sl811h_req, queue); + urb = req->urb; + control = ep->defctrl; + + /* if this frame doesn't have enough time left to transfer this + * packet, wait till the next frame. too-simple algorithm... + */ + fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6; + fclock -= 100; /* setup takes not much time */ + if (urb->dev->speed == USB_SPEED_LOW) { + if (control & SL11H_HCTLMASK_PREAMBLE) { + /* also note erratum 1: some hubs won't work */ + fclock -= 800; + } + fclock -= ep->maxpacket << 8; + + /* erratum 2: AFTERSOF only works for fullspeed */ + if (fclock < 0) { + if (ep->period) + sl811->stat_overrun++; + sofirq_on(sl811); + return NULL; + } + } else { + fclock -= 12000 / 19; /* 19 64byte packets/msec */ + if (fclock < 0) { + if (ep->period) + sl811->stat_overrun++; + control |= SL11H_HCTLMASK_AFTERSOF; + + /* throttle bulk/control irq noise */ + } else if (ep->nak_count) + control |= SL11H_HCTLMASK_AFTERSOF; + } + + + switch (ep->nextpid) { + case USB_PID_IN: + in_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_OUT: + out_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_SETUP: + setup_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_ACK: /* for control status */ + status_packet(sl811, ep, urb, bank, control); + break; + default: + DBG("bad ep%p pid %02x\n", ep, ep->nextpid); + ep = NULL; + } + return ep; +} + +#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2) + +static inline void start_transfer(struct sl811 *sl811) +{ + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) + return; + if (sl811->active_a == NULL) { + sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF)); + if (sl811->active_a != NULL) + sl811->jiffies_a = jiffies + MIN_JIFFIES; + } +#ifdef USE_B + if (sl811->active_b == NULL) { + sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF)); + if (sl811->active_b != NULL) + sl811->jiffies_b = jiffies + MIN_JIFFIES; + } +#endif +} + +static void finish_request( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct sl811h_req *req, + struct pt_regs *regs, + int status +) __releases(sl811->lock) __acquires(sl811->lock) +{ + unsigned i; + struct urb *urb = req->urb; + + list_del(&req->queue); + kfree(req); + urb->hcpriv = NULL; + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = status; + spin_unlock(&urb->lock); + + spin_unlock(&sl811->lock); + usb_hcd_giveback_urb(&sl811->hcd, urb, regs); + spin_lock(&sl811->lock); + + /* leave active endpoints in the schedule */ + if (!list_empty(&ep->queue)) + return; + + /* async deschedule? */ + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + if (ep == sl811->next_async) + sl811->next_async = NULL; + return; + } + + /* periodic deschedule */ + DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct sl811h_ep *temp; + struct sl811h_ep **prev = &sl811->periodic[i]; + + while (*prev && ((temp = *prev) != ep)) + prev = &temp->next; + if (*prev) + *prev = ep->next; + sl811->load[i] -= ep->load; + } + ep->branch = PERIODIC_SIZE; + sl811->periodic_count--; + hcd_to_bus(&sl811->hcd)->bandwidth_allocated + -= ep->load / ep->period; + if (ep == sl811->next_periodic) + sl811->next_periodic = ep->next; + + /* we might turn SOFs back on again for the async schedule */ + if (sl811->periodic_count == 0) + sofirq_off(sl811); +} + +static void +done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs) +{ + u8 status; + struct sl811h_req *req; + struct urb *urb; + int urbstat = -EINPROGRESS; + + if (unlikely(!ep)) + return; + + status = sl811_read(sl811, bank + SL11H_PKTSTATREG); + + req = container_of(ep->queue.next, struct sl811h_req, queue); + urb = req->urb; + + /* we can safely ignore NAKs */ + if (status & SL11H_STATMASK_NAK) { + // PACKET("...NAK_%02x qh%p\n", bank, ep); + if (!ep->period) + ep->nak_count++; + ep->error_count = 0; + + /* ACK advances transfer, toggle, and maybe queue */ + } else if (status & SL11H_STATMASK_ACK) { + struct usb_device *udev = urb->dev; + int len; + unsigned char *buf; + + /* urb->iso_frame_desc is currently ignored here... */ + + ep->nak_count = ep->error_count = 0; + switch (ep->nextpid) { + case USB_PID_OUT: + // PACKET("...ACK/out_%02x qh%p\n", bank, ep); + urb->actual_length += ep->length; + usb_dotoggle(udev, ep->epnum, 1); + if (urb->actual_length + == urb->transfer_buffer_length) { + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_ACK; + + /* some bulk protocols terminate OUT transfers + * by a short packet, using ZLPs not padding. + */ + else if (ep->length < ep->maxpacket + || !(urb->transfer_flags + & URB_ZERO_PACKET)) + urbstat = 0; + } + break; + case USB_PID_IN: + // PACKET("...ACK/in_%02x qh%p\n", bank, ep); + buf = urb->transfer_buffer + urb->actual_length; + prefetchw(buf); + len = ep->maxpacket - sl811_read(sl811, + bank + SL11H_XFERCNTREG); + if (len > ep->length) { + len = ep->length; + urb->status = -EOVERFLOW; + } + urb->actual_length += len; + sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0), + buf, len); + usb_dotoggle(udev, ep->epnum, 0); + if (urb->actual_length == urb->transfer_buffer_length) + urbstat = 0; + else if (len < ep->maxpacket) { + if (urb->transfer_flags & URB_SHORT_NOT_OK) + urbstat = -EREMOTEIO; + else + urbstat = 0; + } + if (usb_pipecontrol(urb->pipe) + && (urbstat == -EREMOTEIO + || urbstat == 0)) { + + /* NOTE if the status stage STALLs (why?), + * this reports the wrong urb status. + */ + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = urbstat; + spin_unlock(&urb->lock); + + req = 0; + ep->nextpid = USB_PID_ACK; + } + break; + case USB_PID_SETUP: + // PACKET("...ACK/setup_%02x qh%p\n", bank, ep); + if (urb->transfer_buffer_length == urb->actual_length) + ep->nextpid = USB_PID_ACK; + else if (usb_pipeout(urb->pipe)) { + usb_settoggle(udev, 0, 1, 1); + ep->nextpid = USB_PID_OUT; + } else { + usb_settoggle(udev, 0, 0, 1); + ep->nextpid = USB_PID_IN; + } + break; + case USB_PID_ACK: + // PACKET("...ACK/status_%02x qh%p\n", bank, ep); + urbstat = 0; + break; + } + + /* STALL stops all transfers */ + } else if (status & SL11H_STATMASK_STALL) { + PACKET("...STALL_%02x qh%p\n", bank, ep); + ep->nak_count = ep->error_count = 0; + urbstat = -EPIPE; + + /* error? retry, until "3 strikes" */ + } else if (++ep->error_count >= 3) { + if (status & SL11H_STATMASK_TMOUT) + urbstat = -ETIMEDOUT; + else if (status & SL11H_STATMASK_OVF) + urbstat = -EOVERFLOW; + else + urbstat = -EPROTO; + ep->error_count = 0; + PACKET("...3STRIKES_%02x %02x qh%p stat %d\n", + bank, status, ep, urbstat); + } + + if ((urbstat != -EINPROGRESS || urb->status != -EINPROGRESS) + && req) + finish_request(sl811, ep, req, regs, urbstat); +} + +static inline u8 checkdone(struct sl811 *sl811) +{ + u8 ctl; + u8 irqstat = 0; + + if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) { + ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)); + if (ctl & SL11H_HCTLMASK_ARM) + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); + DBG("%s DONE_A: ctrl %02x sts %02x\n", + (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", + ctl, + sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); + irqstat |= SL11H_INTMASK_DONE_A; + } +#ifdef USE_B + if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) { + ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)); + if (ctl & SL11H_HCTLMASK_ARM) + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); + DBG("%s DONE_B: ctrl %02x sts %02x\n", ctl, + (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", + ctl, + sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); + irqstat |= SL11H_INTMASK_DONE_A; + } +#endif + return irqstat; +} + +static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs) +{ + struct sl811 *sl811 = _sl811; + u8 irqstat; + irqreturn_t ret = IRQ_NONE; + unsigned retries = 5; + + spin_lock(&sl811->lock); + +retry: + irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP; + if (irqstat) { + sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); + irqstat &= sl811->irq_enable; + } + +#ifdef QUIRK2 + /* this may no longer be necessary ... */ + if (irqstat == 0 && ret == IRQ_NONE) { + irqstat = checkdone(sl811); + if (irqstat && irq != ~0) + sl811->stat_lost++; + } +#endif + + /* USB packets, not necessarily handled in the order they're + * issued ... that's fine if they're different endpoints. + */ + if (irqstat & SL11H_INTMASK_DONE_A) { + done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs); + sl811->active_a = NULL; + sl811->stat_a++; + } +#ifdef USE_B + if (irqstat & SL11H_INTMASK_DONE_B) { + done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs); + sl811->active_b = NULL; + sl811->stat_b++; + } +#endif + if (irqstat & SL11H_INTMASK_SOFINTR) { + unsigned index; + + index = sl811->frame++ % (PERIODIC_SIZE - 1); + sl811->stat_sof++; + + /* be graceful about almost-inevitable periodic schedule + * overruns: continue the previous frame's transfers iff + * this one has nothing scheduled. + */ + if (sl811->next_periodic) { + // ERR("overrun to slot %d\n", index); + sl811->stat_overrun++; + } + if (sl811->periodic[index]) + sl811->next_periodic = sl811->periodic[index]; + } + + /* khubd manages debouncing and wakeup */ + if (irqstat & SL11H_INTMASK_INSRMV) { + sl811->stat_insrmv++; + + /* most stats are reset for each VBUS session */ + sl811->stat_wake = 0; + sl811->stat_sof = 0; + sl811->stat_a = 0; + sl811->stat_b = 0; + sl811->stat_lost = 0; + + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + /* usbcore nukes other pending transactions on disconnect */ + if (sl811->active_a) { + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); + finish_request(sl811, sl811->active_a, + container_of(sl811->active_a->queue.next, + struct sl811h_req, queue), + NULL, -ESHUTDOWN); + sl811->active_a = NULL; + } +#ifdef USE_B + if (sl811->active_b) { + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); + finish_request(sl811, sl811->active_b, + container_of(sl811->active_b->queue.next, + struct sl811h_req, queue), + NULL, -ESHUTDOWN); + sl811->active_b = NULL; + } +#endif + + /* port status seems wierd until after reset, so + * force the reset and make khubd clean up later. + */ + sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) + | (1 << USB_PORT_FEAT_CONNECTION); + + } else if (irqstat & SL11H_INTMASK_RD) { + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) { + DBG("wakeup\n"); + sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND; + sl811->stat_wake++; + } else + irqstat &= ~SL11H_INTMASK_RD; + } + + if (irqstat) { + if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) + start_transfer(sl811); + ret = IRQ_HANDLED; + sl811->hcd.saw_irq = 1; + if (retries--) + goto retry; + } + + if (sl811->periodic_count == 0 && list_empty(&sl811->async)) + sofirq_off(sl811); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + spin_unlock(&sl811->lock); + + return ret; +} + +/*-------------------------------------------------------------------------*/ + +/* usb 1.1 says max 90% of a frame is available for periodic transfers. + * this driver doesn't promise that much since it's got to handle an + * IRQ per packet; irq handling latencies also use up that time. + */ +#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */ + +static int balance(struct sl811 *sl811, u16 period, u16 load) +{ + int i, branch = -ENOSPC; + + /* search for the least loaded schedule branch of that period + * which has enough bandwidth left unreserved. + */ + for (i = 0; i < period ; i++) { + if (branch < 0 || sl811->load[branch] > sl811->load[i]) { + int j; + + for (j = i; j < PERIODIC_SIZE; j += period) { + if ((sl811->load[j] + load) + > MAX_PERIODIC_LOAD) + break; + } + if (j < PERIODIC_SIZE) + continue; + branch = i; + } + } + return branch; +} + +/*-------------------------------------------------------------------------*/ + +static int sl811h_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + int mem_flags +) { + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev = urb->dev; + struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + struct sl811h_ep *ep = NULL; + struct sl811h_req *req; + unsigned long flags; + int i; + int retval = 0; + +#ifdef DISABLE_ISO + if (type == PIPE_ISOCHRONOUS) + return -ENOSPC; +#endif + + /* avoid all allocations within spinlocks: request or endpoint */ + urb->hcpriv = req = kmalloc(sizeof *req, mem_flags); + if (!req) + return -ENOMEM; + req->urb = urb; + + i = epnum << 1; + if (i && is_out) + i |= 1; + if (!hdev->ep[i]) + ep = kcalloc(1, sizeof *ep, mem_flags); + + spin_lock_irqsave(&sl811->lock, flags); + + /* don't submit to a dead or disabled port */ + if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) + || !HCD_IS_RUNNING(sl811->hcd.state)) { + retval = -ENODEV; + goto fail; + } + + if (hdev->ep[i]) { + kfree(ep); + ep = hdev->ep[i]; + } else if (!ep) { + retval = -ENOMEM; + goto fail; + + } else { + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->schedule); + ep->udev = usb_get_dev(udev); + ep->epnum = epnum; + ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; + usb_settoggle(udev, epnum, is_out, 0); + + if (type == PIPE_CONTROL) + ep->nextpid = USB_PID_SETUP; + else if (is_out) + ep->nextpid = USB_PID_OUT; + else + ep->nextpid = USB_PID_IN; + + if (ep->maxpacket > H_MAXPACKET) { + /* iso packets up to 240 bytes could work... */ + DBG("dev %d ep%d maxpacket %d\n", + udev->devnum, epnum, ep->maxpacket); + retval = -EINVAL; + goto fail; + } + + if (udev->speed == USB_SPEED_LOW) { + /* send preamble for external hub? */ + if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD)) + ep->defctrl |= SL11H_HCTLMASK_PREAMBLE; + } + switch (type) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + if (urb->interval > PERIODIC_SIZE) + urb->interval = PERIODIC_SIZE; + ep->period = urb->interval; + ep->branch = PERIODIC_SIZE; + if (type == PIPE_ISOCHRONOUS) + ep->defctrl |= SL11H_HCTLMASK_ISOCH; + ep->load = usb_calc_bus_time(udev->speed, !is_out, + (type == PIPE_ISOCHRONOUS), + usb_maxpacket(udev, pipe, is_out)) + / 1000; + break; + } + + hdev->ep[i] = ep; + } + + /* maybe put endpoint into schedule */ + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + if (list_empty(&ep->schedule)) + list_add_tail(&ep->schedule, &sl811->async); + break; + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + urb->interval = ep->period; + if (ep->branch < PERIODIC_SIZE) + break; + + retval = balance(sl811, ep->period, ep->load); + if (retval < 0) + goto fail; + ep->branch = retval; + retval = 0; + urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) + + ep->branch; + + /* sort each schedule branch by period (slow before fast) + * to share the faster parts of the tree without needing + * dummy/placeholder nodes + */ + DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct sl811h_ep **prev = &sl811->periodic[i]; + struct sl811h_ep *here = *prev; + + while (here && ep != here) { + if (ep->period > here->period) + break; + prev = &here->next; + here = *prev; + } + if (ep != here) { + ep->next = here; + *prev = ep; + } + sl811->load[i] += ep->load; + } + sl811->periodic_count++; + hcd_to_bus(&sl811->hcd)->bandwidth_allocated + += ep->load / ep->period; + sofirq_on(sl811); + } + + /* in case of unlink-during-submit */ + spin_lock(&urb->lock); + if (urb->status != -EINPROGRESS) { + spin_unlock(&urb->lock); + finish_request(sl811, ep, req, NULL, 0); + req = NULL; + retval = 0; + goto fail; + } + list_add_tail(&req->queue, &ep->queue); + spin_unlock(&urb->lock); + + start_transfer(sl811); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); +fail: + spin_unlock_irqrestore(&sl811->lock, flags); + if (retval) + kfree(req); + return retval; +} + +static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev = urb->dev; + struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + unsigned long flags; + unsigned i; + struct sl811h_ep *ep; + struct sl811h_req *req = urb->hcpriv; + int retval = 0; + + i = usb_pipeendpoint(pipe) << 1; + if (i && is_out) + i |= 1; + + spin_lock_irqsave(&sl811->lock, flags); + ep = hdev->ep[i]; + if (ep) { + /* finish right away if this urb can't be active ... + * note that some drivers wrongly expect delays + */ + if (ep->queue.next != &req->queue) { + /* not front of queue? never active */ + + /* for active transfers, we expect an IRQ */ + } else if (sl811->active_a == ep) { + if (time_before_eq(sl811->jiffies_a, jiffies)) { + /* happens a lot with lowspeed?? */ + DBG("giveup on DONE_A: ctrl %02x sts %02x\n", + sl811_read(sl811, + SL811_EP_A(SL11H_HOSTCTLREG)), + sl811_read(sl811, + SL811_EP_A(SL11H_PKTSTATREG))); + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), + 0); + sl811->active_a = NULL; + } else + req = NULL; +#ifdef USE_B + } else if (sl811->active_b == ep) { + if (time_before_eq(sl811->jiffies_a, jiffies)) { + /* happens a lot with lowspeed?? */ + DBG("giveup on DONE_B: ctrl %02x sts %02x\n", + sl811_read(sl811, + SL811_EP_B(SL11H_HOSTCTLREG)), + sl811_read(sl811, + SL811_EP_B(SL11H_PKTSTATREG))); + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), + 0); + sl811->active_b = NULL; + } else + req = NULL; +#endif + } else { + /* front of queue for inactive endpoint */ + } + + if (req) + finish_request(sl811, ep, req, NULL, 0); + else + VDBG("dequeue, urb %p active %s; wait4irq\n", urb, + (sl811->active_a == ep) ? "A" : "B"); + } else + retval = -EINVAL; + spin_unlock_irqrestore(&sl811->lock, flags); + return retval; +} + +static void +sl811h_endpoint_disable(struct usb_hcd *hcd, struct hcd_dev *hdev, int epnum) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct sl811h_ep *ep; + unsigned long flags; + int i; + + i = (epnum & 0xf) << 1; + if (epnum && !(epnum & USB_DIR_IN)) + i |= 1; + + spin_lock_irqsave(&sl811->lock, flags); + ep = hdev->ep[i]; + hdev->ep[i] = NULL; + spin_unlock_irqrestore(&sl811->lock, flags); + + if (ep) { + /* assume we'd just wait for the irq */ + if (!list_empty(&ep->queue)) + msleep(3); + if (!list_empty(&ep->queue)) + WARN("ep %p not empty?\n", ep); + + usb_put_dev(ep->udev); + kfree(ep); + } + return; +} + +static int +sl811h_get_frame(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + + /* wrong except while periodic transfers are scheduled; + * never matches the on-the-wire frame; + * subject to overruns. + */ + return sl811->frame; +} + + +/*-------------------------------------------------------------------------*/ + +/* the virtual root hub timer IRQ checks for hub status */ +static int +sl811h_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); +#ifdef QUIRK3 + unsigned long flags; + + /* non-SMP HACK: use root hub timer as i/o watchdog + * this seems essential when SOF IRQs aren't in use... + */ + local_irq_save(flags); + if (!timer_pending(&sl811->timer)) { + if (sl811h_irq(~0, sl811, NULL) != IRQ_NONE) + sl811->stat_lost++; + } + local_irq_restore(flags); +#endif + + if (!(sl811->port1 & (0xffff << 16))) + return 0; + + /* tell khubd port 1 changed */ + *buf = (1 << 1); + return 1; +} + +static void +sl811h_hub_descriptor ( + struct sl811 *sl811, + struct usb_hub_descriptor *desc +) { + u16 temp = 0; + + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = 1; + desc->bDescLength = 9; + + /* per-port power switching (gang of one!), or none */ + desc->bPwrOn2PwrGood = 0; + if (sl811->board && sl811->board->port_power) { + desc->bPwrOn2PwrGood = sl811->board->potpg; + if (!desc->bPwrOn2PwrGood) + desc->bPwrOn2PwrGood = 10; + temp = 0x0001; + } else + temp = 0x0002; + + /* no overcurrent errors detection/handling */ + temp |= 0x0010; + + desc->wHubCharacteristics = cpu_to_le16(temp); + + /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ + desc->bitmap[0] = 1 << 1; + desc->bitmap[1] = ~0; +} + +static void +sl811h_timer(unsigned long _sl811) +{ + struct sl811 *sl811 = (void *) _sl811; + unsigned long flags; + u8 irqstat; + u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE; + const u32 mask = (1 << USB_PORT_FEAT_CONNECTION) + | (1 << USB_PORT_FEAT_ENABLE) + | (1 << USB_PORT_FEAT_LOWSPEED); + + spin_lock_irqsave(&sl811->lock, flags); + + /* stop special signaling */ + sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + udelay(3); + + irqstat = sl811_read(sl811, SL11H_IRQ_STATUS); + + switch (signaling) { + case SL11H_CTL1MASK_SE0: + DBG("end reset\n"); + sl811->port1 = (1 << USB_PORT_FEAT_C_RESET) + | (1 << USB_PORT_FEAT_POWER); + sl811->ctrl1 = 0; + /* don't wrongly ack RD */ + if (irqstat & SL11H_INTMASK_INSRMV) + irqstat &= ~SL11H_INTMASK_RD; + break; + case SL11H_CTL1MASK_K: + DBG("end resume\n"); + sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND); + break; + default: + DBG("odd timer signaling: %02x\n", signaling); + break; + } + sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); + + if (irqstat & SL11H_INTMASK_RD) { + /* usbcore nukes all pending transactions on disconnect */ + if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) + sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) + | (1 << USB_PORT_FEAT_C_ENABLE); + sl811->port1 &= ~mask; + sl811->irq_enable = SL11H_INTMASK_INSRMV; + } else { + sl811->port1 |= mask; + if (irqstat & SL11H_INTMASK_DP) + sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED); + sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD; + } + + if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) { + u8 ctrl2 = SL811HS_CTL2_INIT; + + sl811->irq_enable |= SL11H_INTMASK_DONE_A; +#ifdef USE_B + sl811->irq_enable |= SL11H_INTMASK_DONE_B; +#endif + if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) { + sl811->ctrl1 |= SL11H_CTL1MASK_LSPD; + ctrl2 |= SL811HS_CTL2MASK_DSWAP; + } + + /* start SOFs flowing, kickstarting with A registers */ + sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA; + sl811_write(sl811, SL11H_SOFLOWREG, 0xe0); + sl811_write(sl811, SL811HS_CTLREG2, ctrl2); + + /* autoincrementing */ + sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0); + writeb(SL_SOF, sl811->data_reg); + writeb(0, sl811->data_reg); + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), + SL11H_HCTLMASK_ARM); + + /* khubd provides debounce delay */ + } else { + sl811->ctrl1 = 0; + } + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + /* reenable irqs */ + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + spin_unlock_irqrestore(&sl811->lock, flags); +} + +static int +sl811h_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct sl811 *sl811 = hcd_to_sl811(hcd); + int retval = 0; + unsigned long flags; + + spin_lock_irqsave(&sl811->lock, flags); + + switch (typeReq) { + case ClearHubFeature: + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + sl811->port1 &= (1 << USB_PORT_FEAT_POWER); + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + break; + case USB_PORT_FEAT_SUSPEND: + if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))) + break; + + /* 20 msec of resume/K signaling, other irqs blocked */ + DBG("start resume...\n"); + sl811->irq_enable = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + sl811->ctrl1 |= SL11H_CTL1MASK_K; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + mod_timer(&sl811->timer, jiffies + + msecs_to_jiffies(20)); + break; + case USB_PORT_FEAT_POWER: + port_power(sl811, 0); + break; + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + break; + default: + goto error; + } + sl811->port1 &= ~(1 << wValue); + break; + case GetHubDescriptor: + sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + *(__le32 *) buf = cpu_to_le32(0); + break; + case GetPortStatus: + if (wIndex != 1) + goto error; + *(__le32 *) buf = cpu_to_le32(sl811->port1); + +#ifndef VERBOSE + if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ +#endif + DBG("GetPortStatus %08x\n", sl811->port1); + break; + case SetPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if (sl811->port1 & (1 << USB_PORT_FEAT_RESET)) + goto error; + if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))) + goto error; + + DBG("suspend...\n"); + sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + break; + case USB_PORT_FEAT_POWER: + port_power(sl811, 1); + break; + case USB_PORT_FEAT_RESET: + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) + goto error; + if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER))) + break; + + /* 50 msec of reset/SE0 signaling, irqs blocked */ + sl811->irq_enable = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + sl811->ctrl1 = SL11H_CTL1MASK_SE0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811->port1 |= (1 << USB_PORT_FEAT_RESET); + mod_timer(&sl811->timer, jiffies + + msecs_to_jiffies(50)); + break; + default: + goto error; + } + sl811->port1 |= 1 << wValue; + break; + + default: +error: + /* "protocol stall" on error */ + retval = -EPIPE; + } + + spin_unlock_irqrestore(&sl811->lock, flags); + return retval; +} + +#ifdef CONFIG_PM + +static int +sl811h_hub_suspend(struct usb_hcd *hcd) +{ + // SOFs off + DBG("%s\n", __FUNCTION__); + return 0; +} + +static int +sl811h_hub_resume(struct usb_hcd *hcd) +{ + // SOFs on + DBG("%s\n", __FUNCTION__); + return 0; +} + +#else + +#define sl811h_hub_suspend NULL +#define sl811h_hub_resume NULL + +#endif + + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILE + +static inline void create_debug_file(struct sl811 *sl811) { } +static inline void remove_debug_file(struct sl811 *sl811) { } + +#else + +#include +#include + +static void dump_irq(struct seq_file *s, char *label, u8 mask) +{ + seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask, + (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "", + (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "", + (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "", + (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "", + (mask & SL11H_INTMASK_RD) ? " rd" : "", + (mask & SL11H_INTMASK_DP) ? " dp" : ""); +} + +static int proc_sl811h_show(struct seq_file *s, void *unused) +{ + struct sl811 *sl811 = s->private; + struct sl811h_ep *ep; + unsigned i; + + seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n", + sl811->hcd.product_desc, + hcd_name, DRIVER_VERSION, + sl811->port1); + + seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv); + seq_printf(s, "current session: done_a %ld done_b %ld " + "wake %ld sof %ld overrun %ld lost %ld\n\n", + sl811->stat_a, sl811->stat_b, + sl811->stat_wake, sl811->stat_sof, + sl811->stat_overrun, sl811->stat_lost); + + spin_lock_irq(&sl811->lock); + + if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND) + seq_printf(s, "(suspended)\n\n"); + else { + u8 t = sl811_read(sl811, SL11H_CTLREG1); + + seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t, + (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "", + ({char *s; switch (t & SL11H_CTL1MASK_FORCE) { + case SL11H_CTL1MASK_NORMAL: s = ""; break; + case SL11H_CTL1MASK_SE0: s = " se0/reset"; break; + case SL11H_CTL1MASK_K: s = " k/resume"; break; + default: s = "j"; break; + }; s; }), + (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "", + (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : ""); + + dump_irq(s, "irq_enable", + sl811_read(sl811, SL11H_IRQ_ENABLE)); + dump_irq(s, "irq_status", + sl811_read(sl811, SL11H_IRQ_STATUS)); + seq_printf(s, "frame clocks remaining: %d\n", + sl811_read(sl811, SL11H_SOFTMRREG) << 6); + } + + seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a, + sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)), + sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); + seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b, + sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)), + sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); + seq_printf(s, "\n"); + list_for_each_entry (ep, &sl811->async, schedule) { + struct sl811h_req *req; + + seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d" + " nak %d err %d\n", + (ep == sl811->active_a) ? "(A) " : "", + (ep == sl811->active_b) ? "(B) " : "", + ep, ep->epnum, + ({ char *s; switch (ep->nextpid) { + case USB_PID_IN: s = "in"; break; + case USB_PID_OUT: s = "out"; break; + case USB_PID_SETUP: s = "setup"; break; + case USB_PID_ACK: s = "status"; break; + default: s = "?"; break; + }; s;}), + ep->maxpacket, + ep->nak_count, ep->error_count); + list_for_each_entry (req, &ep->queue, queue) { + seq_printf(s, " urb%p, %d/%d\n", req->urb, + req->urb->actual_length, + req->urb->transfer_buffer_length); + } + } + if (!list_empty(&sl811->async)) + seq_printf(s, "\n"); + + seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); + + for (i = 0; i < PERIODIC_SIZE; i++) { + ep = sl811->periodic[i]; + if (!ep) + continue; + seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]); + + /* DUMB: prints shared entries multiple times */ + do { + seq_printf(s, + " %s%sqh%d/%p (%sdev%d ep%d%s max %d) " + "err %d\n", + (ep == sl811->active_a) ? "(A) " : "", + (ep == sl811->active_b) ? "(B) " : "", + ep->period, ep, + (ep->udev->speed == USB_SPEED_FULL) + ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == 0) ? "" + : ((ep->nextpid == USB_PID_IN) + ? "in" + : "out"), + ep->maxpacket, ep->error_count); + ep = ep->next; + } while (ep); + } + + spin_unlock_irq(&sl811->lock); + seq_printf(s, "\n"); + + return 0; +} + +static int proc_sl811h_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_sl811h_show, PDE(inode)->data); +} + +static struct file_operations proc_ops = { + .open = proc_sl811h_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* expect just one sl811 per system */ +static const char proc_filename[] = "driver/sl811h"; + +static void create_debug_file(struct sl811 *sl811) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry(proc_filename, 0, NULL); + if (pde == NULL) + return; + + pde->proc_fops = &proc_ops; + pde->data = sl811; + sl811->pde = pde; +} + +static void remove_debug_file(struct sl811 *sl811) +{ + if (sl811->pde) + remove_proc_entry(proc_filename, NULL); +} + +#endif + +/*-------------------------------------------------------------------------*/ + +static void +sl811h_stop(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + unsigned long flags; + + del_timer_sync(&sl811->hcd.rh_timer); + + spin_lock_irqsave(&sl811->lock, flags); + port_power(sl811, 0); + spin_unlock_irqrestore(&sl811->lock, flags); +} + +static int +sl811h_start(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev; + + /* chip has been reset, VBUS power is off */ + + udev = usb_alloc_dev(NULL, &sl811->hcd.self, 0); + if (!udev) + return -ENOMEM; + + udev->speed = USB_SPEED_FULL; + hcd->state = USB_STATE_RUNNING; + + if (sl811->board) + sl811->hcd.can_wakeup = sl811->board->can_wakeup; + + if (hcd_register_root(udev, &sl811->hcd) != 0) { + usb_put_dev(udev); + sl811h_stop(hcd); + return -ENODEV; + } + + if (sl811->board && sl811->board->power) + hub_set_power_budget(udev, sl811->board->power * 2); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct hc_driver sl811h_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .flags = HCD_USB11, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = sl811h_urb_enqueue, + .urb_dequeue = sl811h_urb_dequeue, + .endpoint_disable = sl811h_endpoint_disable, + + /* + * periodic schedule support + */ + .get_frame_number = sl811h_get_frame, + + /* + * root hub support + */ + .hub_status_data = sl811h_hub_status_data, + .hub_control = sl811h_hub_control, + .hub_suspend = sl811h_hub_suspend, + .hub_resume = sl811h_hub_resume, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init_or_module +sl811h_remove(struct device *dev) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + struct platform_device *pdev; + struct resource *res; + + pdev = container_of(dev, struct platform_device, dev); + + if (HCD_IS_RUNNING(sl811->hcd.state)) + sl811->hcd.state = USB_STATE_QUIESCING; + + usb_disconnect(&sl811->hcd.self.root_hub); + remove_debug_file(sl811); + sl811h_stop(&sl811->hcd); + + if (!list_empty(&sl811->hcd.self.bus_list)) + usb_deregister_bus(&sl811->hcd.self); + + if (sl811->hcd.irq >= 0) + free_irq(sl811->hcd.irq, sl811); + + if (sl811->data_reg) + iounmap(sl811->data_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_mem_region(res->start, 1); + + if (sl811->addr_reg) + iounmap(sl811->addr_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, 1); + + kfree(sl811); + return 0; +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __init +sl811h_probe(struct device *dev) +{ + struct sl811 *sl811; + struct platform_device *pdev; + struct resource *addr, *data; + int irq; + int status; + u8 tmp; + unsigned long flags; + + /* basic sanity checks first. board-specific init logic should + * have initialized these three resources and probably board + * specific platform_data. we don't probe for IRQs, and do only + * minimal sanity checking. + */ + pdev = container_of(dev, struct platform_device, dev); + if (pdev->num_resources < 3) + return -ENODEV; + + addr = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq = platform_get_irq(pdev, 0); + if (!addr || !data || irq < 0) + return -ENODEV; + + /* refuse to confuse usbcore */ + if (dev->dma_mask) { + DBG("no we won't dma\n"); + return -EINVAL; + } + + if (!request_mem_region(addr->start, 1, hcd_name)) + return -EBUSY; + if (!request_mem_region(data->start, 1, hcd_name)) { + release_mem_region(addr->start, 1); + return -EBUSY; + } + + /* allocate and initialize hcd */ + sl811 = kcalloc(1, sizeof *sl811, GFP_KERNEL); + if (!sl811) + return 0; + dev_set_drvdata(dev, sl811); + + usb_bus_init(&sl811->hcd.self); + sl811->hcd.self.controller = dev; + sl811->hcd.self.bus_name = dev->bus_id; + sl811->hcd.self.op = &usb_hcd_operations; + sl811->hcd.self.hcpriv = sl811; + + // NOTE: 2.6.11 starts to change the hcd glue layer some more, + // eventually letting us eliminate struct sl811h_req and a + // lot of the boilerplate code here + + INIT_LIST_HEAD(&sl811->hcd.dev_list); + sl811->hcd.self.release = &usb_hcd_release; + + sl811->hcd.description = sl811h_hc_driver.description; + init_timer(&sl811->hcd.rh_timer); + sl811->hcd.driver = &sl811h_hc_driver; + sl811->hcd.irq = -1; + sl811->hcd.state = USB_STATE_HALT; + + spin_lock_init(&sl811->lock); + INIT_LIST_HEAD(&sl811->async); + sl811->board = dev->platform_data; + init_timer(&sl811->timer); + sl811->timer.function = sl811h_timer; + sl811->timer.data = (unsigned long) sl811; + + sl811->addr_reg = ioremap(addr->start, resource_len(addr)); + if (sl811->addr_reg == NULL) { + status = -ENOMEM; + goto fail; + } + sl811->data_reg = ioremap(data->start, resource_len(addr)); + if (sl811->data_reg == NULL) { + status = -ENOMEM; + goto fail; + } + + spin_lock_irqsave(&sl811->lock, flags); + port_power(sl811, 0); + spin_unlock_irqrestore(&sl811->lock, flags); + msleep(200); + + tmp = sl811_read(sl811, SL11H_HWREVREG); + switch (tmp >> 4) { + case 1: + sl811->hcd.product_desc = "SL811HS v1.2"; + break; + case 2: + sl811->hcd.product_desc = "SL811HS v1.5"; + break; + default: + /* reject case 0, SL11S is less functional */ + DBG("chiprev %02x\n", tmp); + status = -ENXIO; + goto fail; + } + + /* sl811s would need a different handler for this irq */ +#ifdef CONFIG_ARM + /* Cypress docs say the IRQ is IRQT_HIGH ... */ + set_irq_type(irq, IRQT_RISING); +#endif + status = request_irq(irq, sl811h_irq, SA_INTERRUPT, hcd_name, sl811); + if (status < 0) + goto fail; + sl811->hcd.irq = irq; + + INFO("%s, irq %d\n", sl811->hcd.product_desc, irq); + + status = usb_register_bus(&sl811->hcd.self); + if (status < 0) + goto fail; + status = sl811h_start(&sl811->hcd); + if (status == 0) { + create_debug_file(sl811); + return 0; + } +fail: + sl811h_remove(dev); + DBG("init error, %d\n", status); + return status; +} + +#ifdef CONFIG_PM + +/* for this device there's no useful distinction between the controller + * and its root hub, except that the root hub only gets direct PM calls + * when CONFIG_USB_SUSPEND is enabled. + */ + +static int +sl811h_suspend(struct device *dev, u32 state, u32 phase) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + int retval = 0; + + if (phase != SUSPEND_POWER_DOWN) + return retval; + + if (state <= PM_SUSPEND_MEM) + retval = sl811h_hub_suspend(&sl811->hcd); + else + port_power(sl811, 0); + if (retval == 0) + dev->power.power_state = state; + return retval; +} + +static int +sl811h_resume(struct device *dev, u32 phase) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + + if (phase != RESUME_POWER_ON) + return 0; + + /* with no "check to see if VBUS is still powered" board hook, + * let's assume it'd only be powered to enable remote wakeup. + */ + if (dev->power.power_state > PM_SUSPEND_MEM + || !sl811->hcd.can_wakeup) { + sl811->port1 = 0; + port_power(sl811, 1); + return 0; + } + + dev->power.power_state = PM_SUSPEND_ON; + return sl811h_hub_resume(&sl811->hcd); +} + +#else + +#define sl811h_suspend NULL +#define sl811h_resume NULL + +#endif + + +static struct device_driver sl811h_driver = { + .name = (char *) hcd_name, + .bus = &platform_bus_type, + + .probe = sl811h_probe, + .remove = sl811h_remove, + + .suspend = sl811h_suspend, + .resume = sl811h_resume, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init sl811h_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION); + return driver_register(&sl811h_driver); +} +module_init(sl811h_init); + +static void __exit sl811h_cleanup(void) +{ + driver_unregister(&sl811h_driver); +} +module_exit(sl811h_cleanup); diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h new file mode 100644 index 000000000000..1f5f0d48de85 --- /dev/null +++ b/drivers/usb/host/sl811.h @@ -0,0 +1,270 @@ +/* + * SL811HS register declarations and HCD data structures + * + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + * Copyright (C) 2001 Cypress Semiconductor Inc. + */ + +/* + * SL811HS has transfer registers, and control registers. In host/master + * mode one set of registers is used; in peripheral/slave mode, another. + * - SL11H only has some "A" transfer registers from 0x00-0x04 + * - SL811HS also has "B" registers from 0x08-0x0c + * - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30 + */ + +#define SL811_EP_A(base) ((base) + 0) +#define SL811_EP_B(base) ((base) + 8) + +#define SL811_HOST_BUF 0x00 +#define SL811_PERIPH_EP0 0x00 +#define SL811_PERIPH_EP1 0x10 +#define SL811_PERIPH_EP2 0x20 +#define SL811_PERIPH_EP3 0x30 + + +/* TRANSFER REGISTERS: host and peripheral sides are similar + * except for the control models (master vs slave). + */ +#define SL11H_HOSTCTLREG 0 +# define SL11H_HCTLMASK_ARM 0x01 +# define SL11H_HCTLMASK_ENABLE 0x02 +# define SL11H_HCTLMASK_IN 0x00 +# define SL11H_HCTLMASK_OUT 0x04 +# define SL11H_HCTLMASK_ISOCH 0x10 +# define SL11H_HCTLMASK_AFTERSOF 0x20 +# define SL11H_HCTLMASK_TOGGLE 0x40 +# define SL11H_HCTLMASK_PREAMBLE 0x80 +#define SL11H_BUFADDRREG 1 +#define SL11H_BUFLNTHREG 2 +#define SL11H_PKTSTATREG 3 /* read */ +# define SL11H_STATMASK_ACK 0x01 +# define SL11H_STATMASK_ERROR 0x02 +# define SL11H_STATMASK_TMOUT 0x04 +# define SL11H_STATMASK_SEQ 0x08 +# define SL11H_STATMASK_SETUP 0x10 +# define SL11H_STATMASK_OVF 0x20 +# define SL11H_STATMASK_NAK 0x40 +# define SL11H_STATMASK_STALL 0x80 +#define SL11H_PIDEPREG 3 /* write */ +# define SL_SETUP 0xd0 +# define SL_IN 0x90 +# define SL_OUT 0x10 +# define SL_SOF 0x50 +# define SL_PREAMBLE 0xc0 +# define SL_NAK 0xa0 +# define SL_STALL 0xe0 +# define SL_DATA0 0x30 +# define SL_DATA1 0xb0 +#define SL11H_XFERCNTREG 4 /* read */ +#define SL11H_DEVADDRREG 4 /* write */ + + +/* CONTROL REGISTERS: host and peripheral are very different. + */ +#define SL11H_CTLREG1 5 +# define SL11H_CTL1MASK_SOF_ENA 0x01 +# define SL11H_CTL1MASK_FORCE 0x18 +# define SL11H_CTL1MASK_NORMAL 0x00 +# define SL11H_CTL1MASK_SE0 0x08 /* reset */ +# define SL11H_CTL1MASK_J 0x10 +# define SL11H_CTL1MASK_K 0x18 /* resume */ +# define SL11H_CTL1MASK_LSPD 0x20 +# define SL11H_CTL1MASK_SUSPEND 0x40 +#define SL11H_IRQ_ENABLE 6 +# define SL11H_INTMASK_DONE_A 0x01 +# define SL11H_INTMASK_DONE_B 0x02 +# define SL11H_INTMASK_SOFINTR 0x10 +# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */ +# define SL11H_INTMASK_RD 0x40 +# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */ +#define SL11S_ADDRESS 7 + +/* 0x08-0x0c are for the B buffer (not in SL11) */ + +#define SL11H_IRQ_STATUS 0x0D /* write to ack */ +#define SL11H_HWREVREG 0x0E /* read */ +# define SL11H_HWRMASK_HWREV 0xF0 +#define SL11H_SOFLOWREG 0x0E /* write */ +#define SL11H_SOFTMRREG 0x0F /* read */ + +/* a write to this register enables SL811HS features. + * HOST flag presumably overrides the chip input signal? + */ +#define SL811HS_CTLREG2 0x0F +# define SL811HS_CTL2MASK_SOF_MASK 0x3F +# define SL811HS_CTL2MASK_DSWAP 0x40 +# define SL811HS_CTL2MASK_HOST 0x80 + +#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e) + + +/* DATA BUFFERS: registers from 0x10..0xff are for data buffers; + * that's 240 bytes, which we'll split evenly between A and B sides. + * Only ISO can use more than 64 bytes per packet. + * (The SL11S has 0x40..0xff for buffers.) + */ +#define H_MAXPACKET 120 /* bytes in A or B fifos */ + +#define SL11H_DATA_START 0x10 +#define SL811HS_PACKET_BUF(is_a) ((is_a) \ + ? SL11H_DATA_START \ + : (SL11H_DATA_START + H_MAXPACKET)) + +/*-------------------------------------------------------------------------*/ + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +struct sl811 { + struct usb_hcd hcd; + spinlock_t lock; + void __iomem *addr_reg; + void __iomem *data_reg; + struct sl811_platform_data *board; + struct proc_dir_entry *pde; + + unsigned long stat_insrmv; + unsigned long stat_wake; + unsigned long stat_sof; + unsigned long stat_a; + unsigned long stat_b; + unsigned long stat_lost; + unsigned long stat_overrun; + + /* sw model */ + struct timer_list timer; + struct sl811h_ep *next_periodic; + struct sl811h_ep *next_async; + + struct sl811h_ep *active_a; + unsigned long jiffies_a; + struct sl811h_ep *active_b; + unsigned long jiffies_b; + + u32 port1; + u8 ctrl1, ctrl2, irq_enable; + u16 frame; + + /* async schedule: control, bulk */ + struct list_head async; + + /* periodic schedule: interrupt, iso */ + u16 load[PERIODIC_SIZE]; + struct sl811h_ep *periodic[PERIODIC_SIZE]; + unsigned periodic_count; +}; + +static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd) +{ + return container_of(hcd, struct sl811, hcd); +} + +struct sl811h_ep { + struct list_head queue; + struct usb_device *udev; + + u8 defctrl; + u8 maxpacket; + u8 epnum; + u8 nextpid; + + u16 error_count; + u16 nak_count; + u16 length; /* of current packet */ + + /* periodic schedule */ + u16 period; + u16 branch; + u16 load; + struct sl811h_ep *next; + + /* async schedule */ + struct list_head schedule; +}; + +struct sl811h_req { + /* FIXME usbcore should maintain endpoints' urb queues + * directly in 'struct usb_host_endpoint' + */ + struct urb *urb; + struct list_head queue; +}; + +/*-------------------------------------------------------------------------*/ + +/* These register utilities should work for the SL811S register API too + * NOTE: caller must hold sl811->lock. + */ + +static inline u8 sl811_read(struct sl811 *sl811, int reg) +{ + writeb(reg, sl811->addr_reg); + return readb(sl811->data_reg); +} + +static inline void sl811_write(struct sl811 *sl811, int reg, u8 val) +{ + writeb(reg, sl811->addr_reg); + writeb(val, sl811->data_reg); +} + +static inline void +sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count) +{ + const u8 *data; + void __iomem *data_reg; + + if (!count) + return; + writeb(addr, sl811->addr_reg); + + data = buf; + data_reg = sl811->data_reg; + do { + writeb(*data++, data_reg); + } while (--count); +} + +static inline void +sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count) +{ + u8 *data; + void __iomem *data_reg; + + if (!count) + return; + writeb(addr, sl811->addr_reg); + + data = buf; + data_reg = sl811->data_reg; + do { + *data++ = readb(data_reg); + } while (--count); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff) +#else +#define DBG(stuff...) do{}while(0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(stuff...) do{}while(0) +#endif + +#ifdef PACKET_TRACE +# define PACKET VDBG +#else +# define PACKET(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff) +#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff) + diff --git a/include/linux/usb_sl811.h b/include/linux/usb_sl811.h new file mode 100644 index 000000000000..4f2d012d7309 --- /dev/null +++ b/include/linux/usb_sl811.h @@ -0,0 +1,26 @@ + +/* + * board initialization should put one of these into dev->platform_data + * and place the sl811hs onto platform_bus named "sl811-hcd". + */ + +struct sl811_platform_data { + unsigned can_wakeup:1; + + /* given port_power, msec/2 after power on till power good */ + u8 potpg; + + /* mA/2 power supplied on this port (max = default = 250) */ + u8 power; + + /* sl811 relies on an external source of VBUS current */ + void (*port_power)(struct device *dev, int is_on); + + /* pulse sl811 nRST (probably with a GPIO) */ + void (*reset)(struct device *dev); + + // some boards need something like these: + // int (*check_overcurrent)(struct device *dev); + // void (*clock_enable)(struct device *dev, int is_on); +}; + -- cgit v1.2.3 From 8774514cbe3c197825dc06097f1d4c8f20e21c8e Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 9 Dec 2004 20:10:10 -0800 Subject: [NETFILTER]: TCP window tracking bug fixes. 1) Name IP_CT_TCP_STATE_FLAG_WINDOW_SCALE more consistently. 2) Client sends SYN, server responds with SYN/ACK. However the SYN/ACK is lost in transit and the client keeps sending the SYNs. The server times out, restarts, and sends SYN/ACK with new sequence numbers. Those packets were however erroneously dropped by the window tracking code. 3) NFS client and server, client crashes and connects to the server from the same port as before the crash. Server thinks the connection is still alove and sends an ACK, client responds with a RST and tears down the connection so that it can start a new one. That was not handled by the previous code. 4) Occasionally the window tracking code logged BUG lines due to a leftover ack instead of sack in the logging part. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv4/ip_conntrack_tcp.h | 2 +- net/ipv4/netfilter/ip_conntrack_proto_tcp.c | 33 ++++++++++++++----------- 2 files changed, 20 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h b/include/linux/netfilter_ipv4/ip_conntrack_tcp.h index 0ab4590a0b16..61dad5790198 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_tcp.h @@ -18,7 +18,7 @@ enum tcp_conntrack { }; /* Window scaling is advertised by the sender */ -#define IP_CT_TCP_STATE_FLAG_WINDOW_SCALE 0x01 +#define IP_CT_TCP_FLAG_WINDOW_SCALE 0x01 /* SACK is permitted by the sender */ #define IP_CT_TCP_FLAG_SACK_PERM 0x02 diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c index 265b583d2f99..fb4d59acd9f0 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c @@ -273,9 +273,9 @@ static enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { * sCL -> sCL */ /* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*ack*/ { sIV, sIV, sIV, sES, sCW, sCW, sTW, sTW, sCL, sIV }, +/*ack*/ { sIV, sIG, sIV, sES, sCW, sCW, sTW, sTW, sCL, sIV }, /* - * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. + * sSS -> sIG Might be a half-open connection. * sSR -> sIV Simultaneous open. * sES -> sES :-) * sFW -> sCW Normal close request answered by ACK. @@ -436,7 +436,7 @@ static void tcp_options(const struct sk_buff *skb, state->td_scale = 14; } state->flags |= - IP_CT_TCP_STATE_FLAG_WINDOW_SCALE; + IP_CT_TCP_FLAG_WINDOW_SCALE; } ptr += opsize - 2; length -= opsize; @@ -552,8 +552,8 @@ static int tcp_in_window(struct ip_ct_tcp *state, * Both sides must send the Window Scale option * to enable window scaling in either direction. */ - if (!(sender->flags & IP_CT_TCP_STATE_FLAG_WINDOW_SCALE - && receiver->flags & IP_CT_TCP_STATE_FLAG_WINDOW_SCALE)) + if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE + && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE)) sender->td_scale = receiver->td_scale = 0; } else { @@ -566,9 +566,11 @@ static int tcp_in_window(struct ip_ct_tcp *state, sender->td_maxwin = (win == 0 ? 1 : win); sender->td_maxend = end + sender->td_maxwin; } - } else if (state->state == TCP_CONNTRACK_SYN_SENT - && dir == IP_CT_DIR_ORIGINAL - && after(end, sender->td_end)) { + } else if (((state->state == TCP_CONNTRACK_SYN_SENT + && dir == IP_CT_DIR_ORIGINAL) + || (state->state == TCP_CONNTRACK_SYN_RECV + && dir == IP_CT_DIR_REPLY)) + && after(end, sender->td_end)) { /* * RFC 793: "if a TCP is reinitialized ... then it need * not wait at all; it must only be sure to use sequence @@ -685,7 +687,7 @@ static int tcp_in_window(struct ip_ct_tcp *state, "ip_ct_tcp: %s ", before(end, sender->td_maxend + 1) ? after(seq, sender->td_end - receiver->td_maxwin - 1) ? - before(ack, receiver->td_end + 1) ? + before(sack, receiver->td_end + 1) ? after(ack, receiver->td_end - MAXACKWINDOW(sender)) ? "BUG" : "ACK is under the lower bound (possibly overly delayed ACK)" : "ACK is over the upper bound (ACKed data has never seen yet)" @@ -846,7 +848,9 @@ static int tcp_packet(struct ip_conntrack *conntrack, switch (new_state) { case TCP_CONNTRACK_IGNORE: - /* Either SYN in ORIGINAL, or SYN/ACK in REPLY direction. */ + /* Either SYN in ORIGINAL + * or SYN/ACK in REPLY + * or ACK in REPLY direction (half-open connection). */ if (index == TCP_SYNACK_SET && conntrack->proto.tcp.last_index == TCP_SYN_SET && conntrack->proto.tcp.last_dir != dir @@ -875,7 +879,7 @@ static int tcp_packet(struct ip_conntrack *conntrack, WRITE_UNLOCK(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, - "ip_ct_tcp: invalid SYN (ignored) "); + "ip_ct_tcp: invalid packet ignored "); return NF_ACCEPT; case TCP_CONNTRACK_MAX: /* Invalid packet */ @@ -900,11 +904,12 @@ static int tcp_packet(struct ip_conntrack *conntrack, break; case TCP_CONNTRACK_CLOSE: if (index == TCP_RST_SET - && test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) - && conntrack->proto.tcp.last_index <= TCP_SYNACK_SET + && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) + && conntrack->proto.tcp.last_index <= TCP_SYNACK_SET) + || conntrack->proto.tcp.last_index == TCP_ACK_SET) && after(ntohl(th->ack_seq), conntrack->proto.tcp.last_seq)) { - /* Ignore RST closing down invalid SYN + /* Ignore RST closing down invalid SYN or ACK we had let trough. */ WRITE_UNLOCK(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) -- cgit v1.2.3 From e40db8a9247c00b8bec0910612aee47c3da4fef8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Dec 2004 21:15:16 +0100 Subject: [ide] atiixp: add new PCI identifier From: Pascal Lengard Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/pci/atiixp.c | 1 + include/linux/pci_ids.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux') diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index ee4dae238360..f7deea91a06b 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -347,6 +347,7 @@ static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_devic static struct pci_device_id atiixp_pci_tbl[] = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 0, }, }; MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index f1946ad646e4..275a1975404b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -344,6 +344,7 @@ #define PCI_DEVICE_ID_ATI_RS300_200 0x5833 /* ATI IXP Chipset */ #define PCI_DEVICE_ID_ATI_IXP_IDE 0x4349 +#define PCI_DEVICE_ID_ATI_IXP2_IDE 0x4369 /* True name not yet sure */ #define PCI_VENDOR_ID_VLSI 0x1004 #define PCI_DEVICE_ID_VLSI_82C592 0x0005 -- cgit v1.2.3 From bb8857fe23ee15122e84fea73a2315a695f42ee3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Dec 2004 21:28:35 +0100 Subject: [ide] alim15x3: add support for ULi M5228 From: Clear Zhang Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/pci/alim15x3.c | 7 +++++-- include/linux/pci_ids.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 51ea019da0cc..095ea1e3ac14 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -8,6 +8,7 @@ * Copyright (C) 1998-2000 Andre Hedrick (andre@linux-ide.org) * May be copied or modified under the terms of the GNU General Public License * Copyright (C) 2002 Alan Cox + * ALi (now ULi M5228) support by Clear Zhang * * (U)DMA capable version of ali 1533/1543(C), 1535(D) * @@ -799,8 +800,9 @@ static void __init init_hwif_ali15x3 (ide_hwif_t *hwif) s8 irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; int irq = -1; - - hwif->irq = hwif->channel ? 15 : 14; + + if (hwif->pci_dev->device == PCI_DEVICE_ID_AL_M5229) + hwif->irq = hwif->channel ? 15 : 14; if (isa_dev) { /* @@ -889,6 +891,7 @@ static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_dev static struct pci_device_id alim15x3_pci_tbl[] = { { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5228, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, alim15x3_pci_tbl); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 275a1975404b..affb193a43e6 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1030,6 +1030,7 @@ #define PCI_DEVICE_ID_AL_M3307 0x3307 #define PCI_DEVICE_ID_AL_M4803 0x5215 #define PCI_DEVICE_ID_AL_M5219 0x5219 +#define PCI_DEVICE_ID_AL_M5228 0x5228 #define PCI_DEVICE_ID_AL_M5229 0x5229 #define PCI_DEVICE_ID_AL_M5237 0x5237 #define PCI_DEVICE_ID_AL_M5243 0x5243 -- cgit v1.2.3 From 541842f9651aafc7bbdc4ac86a6691e7d57f921a Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Sun, 12 Dec 2004 16:31:34 -0800 Subject: [PATCH] Add missing __KERNEL__ (or other) protections As of 2.6.10-rc3, the following is needed to allow various userland packages (sysvinit, dhcp, ppp, libcap, libpcap, lilo) to compile as parts that userland needs (e.g. for ioctls) is in files with stuff userland isn't allowed to see. This adds __KERNEL__ around and some defines ( isn't needed by userland, and is unhappy right now). sysvinit and some other packages need for HDIO_DRIVE_CMD and other IOCTL things. In we were unsafely typedef'ing __le64/__be64 as __u64 only exists when __GNUC__ && !__STRICT_ANSI__ (causing libcap to fail, for example). Finally, provides routines userland simply cannot use on all arches, but is needed by iputils for example. While not all arches put __KERNEL__ around their header, on MIPS including this header currently blows up the build. Signed-off-by: Tom Rini Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/filter.h | 2 ++ include/linux/hdreg.h | 3 ++- include/linux/types.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/filter.h b/include/linux/filter.h index 17218abef8f1..3ba843c46382 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -8,7 +8,9 @@ #include #include +#ifdef __KERNEL__ #include +#endif /* * Current version of the filter code architecture. diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index c94de12a5ee1..b5d660089de4 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -1,6 +1,7 @@ #ifndef _LINUX_HDREG_H #define _LINUX_HDREG_H +#ifdef __KERNEL__ #include /* @@ -57,7 +58,7 @@ #define IO 0x02 #define REL 0x04 #define TAG_MASK 0xf8 - +#endif /* __KERNEL__ */ /* * Command Header sizes for IOCTL commands diff --git a/include/linux/types.h b/include/linux/types.h index 893c4b367bae..dcb13f865df9 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -157,8 +157,10 @@ typedef __u16 __bitwise __le16; typedef __u16 __bitwise __be16; typedef __u32 __bitwise __le32; typedef __u32 __bitwise __be32; +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) typedef __u64 __bitwise __le64; typedef __u64 __bitwise __be64; +#endif struct ustat { __kernel_daddr_t f_tfree; -- cgit v1.2.3 From eb97de0b945cf8a5b1238441c973a7ac90f320c6 Mon Sep 17 00:00:00 2001 From: Michael Hunold Date: Sun, 12 Dec 2004 16:34:24 -0800 Subject: [PATCH] dvb: av7110 driver update - av7110: fixed av7110_before_after_tune()/av7110_fe_lock_fix(): firmware >= 261d: wait for empty message queue, firmware <= 261c: wait 50ms - av7110: add __user and __iomem annotations, remove some unnecessary cast (patch by C.Y.M) - av7110: __av7110_send_fw_cmd(): added some sanity checks suggested by Werner Fin - av7110: added support for full-featured DVB-C cards: 13c2:0000 Siemens DVB-C (full-length card) VES1820/Philips CD1516 and 13c2:0003 Haupauge DVB-C 2.1 VES1820/ALPS TDBE2 - av7110: follow saa7146 changes, remove superflous casts, and other misc. minor cleanups - av7110: Fixed race condition between driver and av7110 while accessing the COMMAND register in DPRAM. See http://www.linuxtv.org/mailinglists/vdr/2004/01-2004/msg00331.html - budget: various cleanups by Adrian bunk Signed-off-by: Michael Hunold Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/media/dvb/ttpci/av7110.c | 166 ++++++++++++-------- drivers/media/dvb/ttpci/av7110.h | 12 +- drivers/media/dvb/ttpci/av7110_hw.c | 92 +++++++++-- drivers/media/dvb/ttpci/av7110_hw.h | 4 + drivers/media/dvb/ttpci/av7110_v4l.c | 124 +++++++++++---- drivers/media/dvb/ttpci/budget-av.c | 26 +--- drivers/media/dvb/ttpci/budget-ci.c | 1 + drivers/media/dvb/ttpci/budget-core.c | 4 +- drivers/media/dvb/ttusb-budget/Kconfig | 2 + drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 176 +++++++++++++++++++++- include/linux/dvb/frontend.h | 2 +- 11 files changed, 468 insertions(+), 141 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 16506f69c743..41ade3783a03 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -77,7 +77,7 @@ static int rgb_on; static int volume = 255; module_param_named(debug, av7110_debug, int, 0644); -MODULE_PARM_DESC(av7110_debug, "Turn on/off debugging (default:off)."); +MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); module_param(vidmode, int, 0444); MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); module_param(pids_off, int, 0444); @@ -117,7 +117,7 @@ static void init_av7110_av(struct av7110 *av7110) /* handle different card types */ /* remaining inits according to card and frontend type */ - av7110->has_analog_tuner = 0; + av7110->analog_tuner_flags = 0; av7110->current_input = 0; if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", @@ -149,10 +149,12 @@ static void init_av7110_av(struct av7110 *av7110) // switch DVB SCART on av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); - if (rgb_on) + if (rgb_on && + (av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 } + } av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); av7110_setup_irc_config(av7110, 0); @@ -192,9 +194,10 @@ static int arm_thread(void *data) av7110->arm_thread = current; - while (1) { - timeout = wait_event_interruptible_timeout(av7110->arm_wait,0 != av7110->arm_rmmod, 5*HZ); - if (-ERESTARTSYS == timeout || 0 != av7110->arm_rmmod) { + for (;;) { + timeout = wait_event_interruptible_timeout(av7110->arm_wait, + av7110->arm_rmmod, 5 * HZ); + if (-ERESTARTSYS == timeout || av7110->arm_rmmod) { /* got signal or told to quit*/ break; } @@ -287,7 +290,7 @@ static void IR_handle(struct av7110 *av7110, u32 ircom) * IRQ handling ****************************************************************************/ -static inline int DvbDmxFilterCallback(u8 * buffer1, size_t buffer1_len, +static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, u8 * buffer2, size_t buffer2_len, struct dvb_demux_filter *dvbdmxfilter, enum dmx_success success, @@ -355,9 +358,8 @@ static void debiirq (unsigned long data) // dprintk(4, "%p\n",av7110); print_time("debi"); - saa7146_write(av7110->dev, IER, - saa7146_read(av7110->dev, IER) & ~MASK_19 ); - saa7146_write(av7110->dev, ISR, MASK_19 ); + SAA7146_IER_DISABLE(av7110->dev, MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); if (type==-1) { printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", @@ -493,9 +495,8 @@ static void gpioirq (unsigned long data) ARM_ClearIrq(av7110); - saa7146_write(av7110->dev, IER, - saa7146_read(av7110->dev, IER) & ~MASK_19 ); - saa7146_write(av7110->dev, ISR, MASK_19 ); + SAA7146_IER_DISABLE(av7110->dev, MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); @@ -822,9 +823,13 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) buf[3] = mode; ret = av7110_fw_request(av7110, buf, 20, &handle, 1); - if (ret < 0) { - dprintk(1, "StartHWFilter error\n"); - return ret; + if (ret != 0 || handle >= 32) { + printk("dvb-ttpci: %s error buf %04x %04x %04x %04x " + "ret %x handle %04x\n", + __FUNCTION__, buf[0], buf[1], buf[2], buf[3], + ret, handle); + dvbdmxfilter->hw_handle = 0xffff; + return -1; } av7110->handle2filter[handle] = dvbdmxfilter; @@ -844,8 +849,9 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) dprintk(4, "%p\n", av7110); handle = dvbdmxfilter->hw_handle; - if (handle > 32) { - dprintk(1, "StopHWFilter tried to stop invalid filter %d, filter type = %d\n", handle, dvbdmxfilter->type); + if (handle >= 32) { + printk("%s tried to stop invalid filter %04x, filter type = %x\n", + __FUNCTION__, handle, dvbdmxfilter->type); return 0; } @@ -855,11 +861,11 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) buf[1] = 1; buf[2] = handle; ret = av7110_fw_request(av7110, buf, 3, answ, 2); - if (ret) - dprintk(1, "StopHWFilter error\n"); - - if (answ[1] != handle) { - dprintk(2, "filter %d shutdown error :%d\n", handle, answ[1]); + if (ret != 0 || answ[1] != handle) { + printk("dvb-ttpci: %s error cmd %04x %04x %04x ret %x " + "resp %04x %04x pid %d\n", + __FUNCTION__, buf[0], buf[1], buf[2], ret, + answ[0], answ[1], dvbdmxfilter->feed->pid); ret = -1; } return ret; @@ -937,7 +943,7 @@ static void dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) static int av7110_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = (struct av7110 *) demux->priv; + struct av7110 *av7110 = demux->priv; dprintk(4, "%p\n", av7110); @@ -995,7 +1001,7 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) static int av7110_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = (struct av7110 *) demux->priv; + struct av7110 *av7110 = demux->priv; dprintk(4, "%p\n", av7110); @@ -1119,18 +1125,20 @@ static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) return 0; } -static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); return 0; } -static int av7110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +static int av7110_diseqc_send_burst(struct dvb_frontend* fe, + fe_sec_mini_cmd_t minicmd) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_diseqc_send(av7110, 0, NULL, minicmd); @@ -1223,7 +1231,8 @@ static void dvb_unregister(struct av7110 *av7110) dvb_dmxdev_release(&av7110->dmxdev); dvb_dmx_release(&av7110->demux); - if (av7110->fe != NULL) dvb_unregister_frontend(av7110->fe); + if (av7110->fe != NULL) + dvb_unregister_frontend(av7110->fe); dvb_unregister_device(av7110->osd_dev); av7110_av_unregister(av7110); av7110_ca_unregister(av7110); @@ -1519,7 +1528,7 @@ static struct stv0299_config alps_bsru6_config = { static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -1547,9 +1556,10 @@ static struct ves1820_config alps_tdbe2_config = { -static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_451_pll_set(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -1572,9 +1582,10 @@ static struct tda8083_config grundig_29504_451_config = { -static int philips_cd1516_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int philips_cd1516_pll_set(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; u32 div; u32 f = params->frequency; u8 data[4]; @@ -1604,7 +1615,7 @@ static struct ves1820_config philips_cd1516_config = { static int alps_tdlb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; u32 div, pwr; u8 data[4]; struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -1644,19 +1655,18 @@ static struct sp8870_config alps_tdlb7_config = { static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; int i; - // this calculation does not match the TDA6405TS datasheet! div = (params->frequency + 36150000 + 31250) / 62500; data[0] = (div >> 8) & 0x7f; data[1] = div & 0xff; - data[2] = 0xce; // this value does not match the TDA6405TS datasheet! + data[2] = 0xce; if (params->frequency < 45000000) return -EINVAL; @@ -1696,9 +1706,14 @@ static struct stv0297_config nexusca_stv0297_config = { static void av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) { - msleep (50); + int synced = (status & FE_HAS_LOCK) ? 1 : 0; - av7110->fe_synced = (status & FE_HAS_LOCK) ? 1 : 0; + av7110->fe_status = status; + + if (av7110->fe_synced == synced) + return; + + av7110->fe_synced = synced; if (av7110->playing) return; @@ -1714,16 +1729,23 @@ static void av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); } else { SetPIDs(av7110, 0, 0, 0, 0, 0); - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, FlushTSQueue, 0); + av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); + av7110_wait_msgstate(av7110, GPMQBusy); } - av7110->fe_status = status; up(&av7110->pid_mutex); } +static int av7110_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct av7110* av7110 = fe->dvb->priv; + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_set_frontend(fe, params); +} + static int av7110_fe_init(struct dvb_frontend* fe) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_fe_lock_fix(av7110, 0); return av7110->fe_init(fe); @@ -1731,7 +1753,7 @@ static int av7110_fe_init(struct dvb_frontend* fe) static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; int ret; /* call the real implementation */ @@ -1748,15 +1770,16 @@ static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_fe_lock_fix(av7110, 0); return av7110->fe_diseqc_reset_overload(fe); } -static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_fe_lock_fix(av7110, 0); return av7110->fe_diseqc_send_master_cmd(fe, cmd); @@ -1764,7 +1787,7 @@ static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_ static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_fe_lock_fix(av7110, 0); return av7110->fe_diseqc_send_burst(fe, minicmd); @@ -1772,7 +1795,7 @@ static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_ static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_fe_lock_fix(av7110, 0); return av7110->fe_set_tone(fe, tone); @@ -1780,7 +1803,7 @@ static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_fe_lock_fix(av7110, 0); return av7110->fe_set_voltage(fe, voltage); @@ -1788,7 +1811,7 @@ static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t volta static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned int cmd) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; av7110_fe_lock_fix(av7110, 0); return av7110->fe_dishnetwork_send_legacy_command(fe, cmd); @@ -1812,9 +1835,8 @@ static void frontend_init(struct av7110 *av7110) if (av7110->dev->pci->subsystem_vendor == 0x110a) { switch(av7110->dev->pci->subsystem_device) { case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) - av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) - break; + av7110->fe = ves1820_attach(&philips_cd1516_config, + &av7110->i2c_adap, read_pwm(av7110)); break; } @@ -1850,27 +1872,41 @@ static void frontend_init(struct av7110 *av7110) av7110->fe->ops->set_tone = av7110_set_tone; break; } + + /* Try DVB-C cards */ + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: + /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ + av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, + read_pwm(av7110)); + break; + case 0x0003: + /* Haupauge DVB-C 2.1 VES1820/ALPS TDBE2 */ + av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, + read_pwm(av7110)); + break; + } break; case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X // ALPS TDLB7 av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap); - if (av7110->fe) - break; break; case 0x0002: // Hauppauge/TT DVB-C premium rev2.X av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) - break; break; case 0x000A: // Hauppauge/TT Nexus-CA rev1.X av7110->fe = stv0297_attach(&nexusca_stv0297_config, &av7110->i2c_adap, 0x7b); if (av7110->fe) { + /* set TDA9819 into DVB mode */ + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) + /* tuner on this needs a slower i2c bus speed */ av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; break; @@ -1893,6 +1929,8 @@ static void frontend_init(struct av7110 *av7110) FE_FUNC_OVERRIDE(av7110->fe->ops->set_tone, av7110->fe_set_tone, av7110_fe_set_tone); FE_FUNC_OVERRIDE(av7110->fe->ops->set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage;) FE_FUNC_OVERRIDE(av7110->fe->ops->dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); + FE_FUNC_OVERRIDE(av7110->fe->ops->set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); + if (dvb_register_frontend(av7110->dvb_adapter, av7110->fe)) { printk("av7110: Frontend registration failed!\n"); if (av7110->fe->ops->release) @@ -1969,7 +2007,6 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d /* locks for data transfers from/to AV7110 */ spin_lock_init (&av7110->debilock); sema_init(&av7110->dcomlock, 1); - av7110->debilock=SPIN_LOCK_UNLOCKED; av7110->debitype=-1; /* default OSD window */ @@ -2086,11 +2123,8 @@ static int av7110_detach (struct saa7146_dev* saa) dvb_unregister(av7110); - IER_DISABLE(saa, (MASK_19 | MASK_03)); -// saa7146_write (av7110->dev, IER, -// saa7146_read(av7110->dev, IER) & ~(MASK_19 | MASK_03)); - - saa7146_write(av7110->dev, ISR,(MASK_19 | MASK_03)); + SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); av7110_ca_exit(av7110); av7110_av_exit(av7110); @@ -2117,7 +2151,7 @@ static int av7110_detach (struct saa7146_dev* saa) static void av7110_irq(struct saa7146_dev* dev, u32 *isr) { - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + struct av7110 *av7110 = dev->ext_priv; if (*isr & MASK_19) tasklet_schedule (&av7110->debi_tasklet); diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h index f755c7fe0cd3..f25a825d7f41 100644 --- a/drivers/media/dvb/ttpci/av7110.h +++ b/drivers/media/dvb/ttpci/av7110.h @@ -34,6 +34,11 @@ #include + +#define ANALOG_TUNER_VES1820 1 +#define ANALOG_TUNER_STV0297 2 +#define ANALOG_TUNER_VBI 0x100 + extern int av7110_debug; #define dprintk(level,args...) \ @@ -82,7 +87,7 @@ struct av7110 { char *card_name; /* support for analog module of dvb-c */ - int has_analog_tuner; + int analog_tuner_flags; int current_input; u32 current_freq; @@ -122,8 +127,8 @@ struct av7110 { spinlock_t debilock; struct semaphore dcomlock; - int debitype; - int debilen; + volatile int debitype; + volatile int debilen; /* Recording and playback flags */ @@ -235,6 +240,7 @@ struct av7110 { int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned int cmd); + int (*fe_set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 36b076788a70..c6f0160f82f4 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -110,16 +110,16 @@ void av7110_reset_arm(struct av7110 *av7110) saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); /* Disable DEBI and GPIO irq */ - IER_DISABLE(av7110->dev, (MASK_19 | MASK_03)); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); + SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); msleep(30); /* the firmware needs some time to initialize */ ARM_ResetMailBox(av7110); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); - IER_ENABLE(av7110->dev, MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); av7110->arm_ready = 1; dprintk(1, "reset ARM\n"); @@ -223,8 +223,8 @@ int av7110_bootarm(struct av7110 *av7110) saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); /* Disable DEBI and GPIO irq */ - IER_DISABLE(av7110->dev, MASK_03 | MASK_19); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); + SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); /* enable DEBI */ saa7146_write(av7110->dev, MC1, 0x08800880); @@ -280,8 +280,8 @@ int av7110_bootarm(struct av7110 *av7110) //ARM_ClearIrq(av7110); ARM_ResetMailBox(av7110); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); - IER_ENABLE(av7110->dev, MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); av7110->arm_errors = 0; av7110->arm_ready = 1; @@ -293,13 +293,44 @@ int av7110_bootarm(struct av7110 *av7110) * DEBI command polling ****************************************************************************/ +int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) +{ + unsigned long start; + u32 stat; + + if (FW_VERSION(av7110->arm_app) <= 0x261c) { + /* not supported by old firmware */ + msleep(50); + return 0; + } + + /* new firmware */ + start = jiffies; + for (;;) { + if (down_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + up(&av7110->dcomlock); + if ((stat & flags) == 0) { + break; + } + if (time_after(jiffies, start + ARM_WAIT_FREE)) { + printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", + __FUNCTION__, stat & flags); + return -1; + } + msleep(1); + } + return 0; +} + int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) { int i; unsigned long start; -#ifdef COM_DEBUG + char *type = NULL; + u16 flags[2] = {0, 0}; u32 stat; -#endif // dprintk(4, "%p\n", av7110); @@ -330,14 +361,45 @@ int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) } #endif + switch ((buf[0] >> 8) & 0xff) { + case COMTYPE_PIDFILTER: + case COMTYPE_ENCODER: + case COMTYPE_REC_PLAY: + case COMTYPE_MPEGDECODER: + type = "MSG"; + flags[0] = GPMQOver; + flags[1] = GPMQFull; + break; + case COMTYPE_OSD: + type = "OSD"; + flags[0] = OSDQOver; + flags[1] = OSDQFull; + break; + default: + break; + } + + if (type != NULL) { + /* non-immediate COMMAND type */ start = jiffies; - while (rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2) & OSDQFull) { - msleep(1); - if (time_after(jiffies, start + ARM_WAIT_OSD)) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for !OSDQFull\n", __FUNCTION__); + for (;;) { + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & flags[0]) { + printk(KERN_ERR "%s: %s QUEUE overflow\n", + __FUNCTION__, type); + return -1; + } + if ((stat & flags[1]) == 0) + break; + if (time_after(jiffies, start + ARM_WAIT_FREE)) { + printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", + __FUNCTION__, type); return -1; } + msleep(1); + } } + for (i = 2; i < length; i++) wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); @@ -972,7 +1034,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) goto out; } else { int i, len = dc->x0-dc->color+1; - u8 __user *colors = dc->data; + u8 __user *colors = (u8 *)dc->data; u8 r, g, b, blend; for (i = 0; i= 261d */ +#define HPQBusy 0x0080 +#define OSDQBusy 0x0100 /* hw section filter flags */ #define SECTION_EIT 0x01 @@ -368,6 +371,7 @@ extern int av7110_firmversion(struct av7110 *av7110); #define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) #define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) +extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); extern int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length); extern int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length); diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index 8d9d2283f591..822ccda93a76 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -92,10 +92,8 @@ static struct v4l2_input inputs[2] = { } }; -/* for Siemens DVB-C analog module: (taken from ves1820.c) */ -static int ves1820_writereg(struct saa7146_dev *dev, u8 reg, u8 data) +static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) { - u8 addr = 0x09; u8 buf[] = { 0x00, reg, data }; struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; @@ -106,6 +104,17 @@ static int ves1820_writereg(struct saa7146_dev *dev, u8 reg, u8 data) return 0; } +static int stv0297_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) +{ + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 2 }; + + if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1)) + return -1; + return 0; +} + + static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) { struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; @@ -117,12 +126,7 @@ static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) return 0; } - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 62.5 kHz. - */ -static int tuner_set_tv_freq(struct saa7146_dev *dev, u32 freq) +static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) { u32 div; u8 config; @@ -151,6 +155,34 @@ static int tuner_set_tv_freq(struct saa7146_dev *dev, u32 freq) return tuner_write(dev, 0x61, buf); } +static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) +{ + u32 div; + u8 data[4]; + + div = (freq + 38900000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xce; + + if (freq < 45000000) + return -EINVAL; + else if (freq < 137000000) + data[3] = 0x01; + else if (freq < 403000000) + data[3] = 0x02; + else if (freq < 860000000) + data[3] = 0x04; + else + return -EINVAL; + + stv0297_writereg(dev, 0x1C, 0x87, 0x78); + stv0297_writereg(dev, 0x1C, 0x86, 0xc8); + return tuner_write(dev, 0x63, data); +} + + static struct saa7146_standard analog_standard[]; static struct saa7146_standard dvb_standard[]; @@ -168,7 +200,6 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh) struct saa7146_vv *vv = dev->vv_data; struct av7110 *av7110 = (struct av7110*)dev->ext_priv; u16 adswitch; - u8 band = 0; int source, sync, err; dprintk(4, "%p\n", av7110); @@ -184,7 +215,6 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh) if (0 != av7110->current_input) { adswitch = 1; - band = 0x60; /* analog band */ source = SAA7146_HPS_SOURCE_PORT_B; sync = SAA7146_HPS_SYNC_PORT_B; memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); @@ -195,9 +225,16 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh) msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) + dprintk(1, "setting band in demodulator failed.\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9198 pin30(VIF) + } } else { adswitch = 0; - band = 0x20; /* digital band */ source = SAA7146_HPS_SOURCE_PORT_A; sync = SAA7146_HPS_SYNC_PORT_A; memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); @@ -208,15 +245,20 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh) msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed.\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) + } } /* hmm, this does not do anything!? */ if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) dprintk(1, "ADSwitch error\n"); - if (ves1820_writereg(dev, 0x0f, band)) - dprintk(1, "setting band in demodulator failed.\n"); - saa7146_set_hps_source_and_sync(dev, source, sync); if (vv->ov_suspend != NULL) { @@ -242,7 +284,7 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); - if (!av7110->has_analog_tuner || t->index != 0) + if (!av7110->analog_tuner_flags || t->index != 0) return -EINVAL; memset(t, 0, sizeof(*t)); @@ -285,7 +327,7 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) u16 fm_matrix, src; dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); - if (!av7110->has_analog_tuner || av7110->current_input != 1) + if (!av7110->analog_tuner_flags || av7110->current_input != 1) return -EINVAL; switch (t->audmode) { @@ -322,7 +364,7 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency); - if (!av7110->has_analog_tuner || av7110->current_input != 1) + if (!av7110->analog_tuner_flags || av7110->current_input != 1) return -EINVAL; memset(f, 0, sizeof(*f)); @@ -336,7 +378,7 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency); - if (!av7110->has_analog_tuner || av7110->current_input != 1) + if (!av7110->analog_tuner_flags || av7110->current_input != 1) return -EINVAL; if (V4L2_TUNER_ANALOG_TV != f->type) @@ -346,7 +388,11 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); /* tune in desired frequency */ - tuner_set_tv_freq(dev, f->frequency); + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + ves1820_set_tv_freq(dev, f->frequency); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + stv0297_set_tv_freq(dev, f->frequency); + } av7110->current_freq = f->frequency; msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); // start stereo detection @@ -361,7 +407,7 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); - if (av7110->has_analog_tuner ) { + if (av7110->analog_tuner_flags) { if (i->index < 0 || i->index >= 2) return -EINVAL; } else { @@ -386,7 +432,7 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_S_INPUT: %d\n", input); - if (!av7110->has_analog_tuner ) + if (!av7110->analog_tuner_flags) return 0; if (input < 0 || input >= 2) @@ -528,7 +574,27 @@ int av7110_init_analog_module(struct av7110 *av7110) INFO(("saa7113 not accessible.\n")); } else { u8 *i = saa7113_init_regs; - av7110->has_analog_tuner = 1; + + if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { + /* Fujitsu/Siemens DVB-Cable */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; + } + + /* setup for DVB by default */ + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed.\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) + } + /* init the saa7113 */ while (*i != 0xff) { if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { @@ -579,7 +645,7 @@ int av7110_init_v4l(struct av7110 *av7110) /* special case DVB-C: these cards have an analog tuner plus need some special handling, so we have separate saa7146_ext_vv data for these... */ - if (av7110->has_analog_tuner) + if (av7110->analog_tuner_flags) ret = saa7146_vv_init(dev, &av7110_vv_data_c); else ret = saa7146_vv_init(dev, &av7110_vv_data_st); @@ -594,12 +660,12 @@ int av7110_init_v4l(struct av7110 *av7110) saa7146_vv_release(dev); return -ENODEV; } - if (av7110->has_analog_tuner) { + if (av7110->analog_tuner_flags) { if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) { ERR(("cannot register vbi v4l2 device. skipping.\n")); - } else - /* we use this to remember that this dvb-c card can do vbi */ - av7110->has_analog_tuner = 2; + } else { + av7110->analog_tuner_flags |= ANALOG_TUNER_VBI; + } } return 0; } @@ -607,7 +673,7 @@ int av7110_init_v4l(struct av7110 *av7110) int av7110_exit_v4l(struct av7110 *av7110) { saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); - if (2 == av7110->has_analog_tuner) + if (av7110->analog_tuner_flags & ANALOG_TUNER_VBI) saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); return 0; } diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index ae790a71afb4..ae9260094d76 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -563,23 +563,13 @@ static int philips_tu1216_pll_init(struct dvb_frontend *fe) { struct budget *budget = (struct budget *) fe->dvb->priv; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = - sizeof(tu1216_init) }; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; // setup PLL configuration if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); - // disable the mc44BC374c (do not check for errors) - tuner_msg.addr = 0x65; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) { - i2c_transfer(&budget->i2c_adap, &tuner_msg, 1); - } - return 0; } @@ -593,7 +583,7 @@ static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p u8 band, cp, filter; // determine charge pump - tuner_frequency = params->frequency + 36130000; + tuner_frequency = params->frequency + 36166000; if (tuner_frequency < 87000000) return -EINVAL; else if (tuner_frequency < 130000000) @@ -620,7 +610,7 @@ static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p // determine band if (params->frequency < 49000000) return -EINVAL; - else if (params->frequency < 159000000) + else if (params->frequency < 161000000) band = 1; else if (params->frequency < 444000000) band = 2; @@ -632,17 +622,14 @@ static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p // setup PLL filter switch (params->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: - tda1004x_write_byte(fe, 0x0C, 0); filter = 0; break; case BANDWIDTH_7_MHZ: - tda1004x_write_byte(fe, 0x0C, 0); filter = 0; break; case BANDWIDTH_8_MHZ: - tda1004x_write_byte(fe, 0x0C, 0xFF); filter = 1; break; @@ -651,11 +638,11 @@ static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p } // calculate divisor - // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = (((params->frequency / 1000) * 6) + 217280) / 1000; + // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((params->frequency / 1000) * 6) + 217496) / 1000; // setup tuner buffer - tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; tuner_buf[1] = tuner_frequency & 0xff; tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; @@ -679,6 +666,7 @@ struct tda1004x_config philips_tu1216_config = { .demod_address = 0x8, .invert = 1, + .invert_oclk = 1, .pll_init = philips_tu1216_pll_init, .pll_set = philips_tu1216_pll_set, .request_firmware = philips_tu1216_request_firmware, diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index c04e26611524..898a8bf3b466 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -844,6 +844,7 @@ static struct tda1004x_config philips_tdm1316l_config = { .demod_address = 0x8, .invert = 0, + .invert_oclk = 0, .pll_init = philips_tdm1316l_pll_init, .pll_set = philips_tdm1316l_pll_set, .request_firmware = philips_tdm1316l_request_firmware, diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c index a70a3fb5e7f8..bba2822e9d6b 100644 --- a/drivers/media/dvb/ttpci/budget-core.c +++ b/drivers/media/dvb/ttpci/budget-core.c @@ -56,7 +56,7 @@ static int stop_ts_capture(struct budget *budget) return budget->feeding; saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off - IER_DISABLE(budget->dev, MASK_10); + SAA7146_IER_DISABLE(budget->dev, MASK_10); return 0; } @@ -124,7 +124,7 @@ static int start_ts_capture (struct budget *budget) saa7146_write(dev, MC2, (MASK_04 | MASK_20)); saa7146_write(dev, MC1, (MASK_04 | MASK_20)); // DMA3 on - IER_ENABLE(budget->dev, MASK_10); // VPE + SAA7146_IER_ENABLE(budget->dev, MASK_10); // VPE return ++budget->feeding; } diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig index 1ecad02789a1..b4d3322b3b76 100644 --- a/drivers/media/dvb/ttusb-budget/Kconfig +++ b/drivers/media/dvb/ttusb-budget/Kconfig @@ -3,6 +3,8 @@ config DVB_TTUSB_BUDGET depends on DVB_CORE && USB select DVB_CX22700 select DVB_TDA1004X + select DVB_TDA8083 + select DVB_STV0299 help Support for external USB adapters designed by Technotrend and produced by Hauppauge, shipped under the brand name 'Nova-USB'. diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index cfa3971e1b91..91a00d99f2ab 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -26,6 +26,8 @@ #include "dvb_net.h" #include "cx22700.h" #include "tda1004x.h" +#include "stv0299.h" +#include "tda8083.h" #include #include @@ -489,7 +491,6 @@ static int ttusb_send_diseqc(struct dvb_frontend* fe, } #endif -#if 0 static int ttusb_update_lnb(struct ttusb *ttusb) { u8 b[] = { 0xaa, ++ttusb->c, 0x16, 5, /*power: */ 1, @@ -524,7 +525,6 @@ static int ttusb_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) return ttusb_update_lnb(ttusb); } #endif -#endif #if 0 @@ -1177,7 +1177,8 @@ static int philips_tdm1316l_pll_set(struct dvb_frontend* fe, struct dvb_frontend tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; - if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) return -EIO; + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; msleep(1); return 0; @@ -1194,24 +1195,187 @@ static struct tda1004x_config philips_tdm1316l_config = { .demod_address = 0x8, .invert = 1, + .invert_oclk = 0, .pll_init = philips_tdm1316l_pll_init, .pll_set = philips_tdm1316l_pll_set, .request_firmware = philips_tdm1316l_request_firmware, }; +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff +}; + +static int alps_bsru6_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 int alps_bsru6_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) + return -EINVAL; + + div = (params->frequency + (125 - 1)) / 125; // round correctly + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0xC4; + + if (params->frequency > 1530000) + buf[3] = 0xc0; + + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct stv0299_config alps_bsru6_config = { + + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, + .pll_set = alps_bsru6_pll_set, +}; + +static int ttusb_novas_grundig_29504_491_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + div = params->frequency / 125; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x8e; + buf[3] = 0x00; + + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct tda8083_config ttusb_novas_grundig_29504_491_config = { + + .demod_address = 0x68, + .pll_set = ttusb_novas_grundig_29504_491_pll_set, +}; + + static void frontend_init(struct ttusb* ttusb) { switch(ttusb->dev->descriptor.idProduct) { + case 0x1003: // Hauppauge/TT Nova-USB-S budget (stv0299/ALPS BSRU6(tsa5059) + // try the ALPS BSRU6 first + ttusb->fe = stv0299_attach(&alps_bsru6_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops->set_voltage = ttusb_set_voltage; + break; + } + + // Grundig 29504-491 + ttusb->fe = tda8083_attach(&ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops->set_voltage = ttusb_set_voltage; + break; + } + + break; + case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) // try the ALPS TDMB7 first ttusb->fe = cx22700_attach(&alps_tdmb7_config, &ttusb->i2c_adap); - if (ttusb->fe != NULL) break; + if (ttusb->fe != NULL) + break; // Philips td1316 ttusb->fe = tda10046_attach(&philips_tdm1316l_config, &ttusb->i2c_adap); - if (ttusb->fe != NULL) break; + if (ttusb->fe != NULL) + break; break; } @@ -1382,7 +1546,7 @@ static void ttusb_disconnect(struct usb_interface *intf) } static struct usb_device_id ttusb_table[] = { -/* {USB_DEVICE(0xb48, 0x1003)},UNDEFINED HARDWARE - mail linuxtv.org list */ + {USB_DEVICE(0xb48, 0x1003)}, /* {USB_DEVICE(0xb48, 0x1004)},UNDEFINED HARDWARE - mail linuxtv.org list*/ /* to be confirmed ???? */ {USB_DEVICE(0xb48, 0x1005)}, {} diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 585560022ba0..fd0bc530dda8 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -62,7 +62,7 @@ typedef enum fe_caps { FE_CAN_HIERARCHY_AUTO = 0x100000, FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, - FE_NEEDS_BENDING = 0x20000000, // frontend requires frequency bending + FE_NEEDS_BENDING = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending) FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output } fe_caps_t; -- cgit v1.2.3 From 09edaa446d946ad240e7f67b4a52de026d406905 Mon Sep 17 00:00:00 2001 From: Markus Lidel Date: Thu, 16 Dec 2004 17:20:23 -0800 Subject: [PATCH] i2o: increase timeout for LCT_NOTIFY Some users have reported problems with the I2O subsystem, which was caused by the timeout to fetch the LCT. - increases the timeout for I2O LCT_GET. On some systems the time to get a reply from the I2O controller is longer then the timeout to wait. Signed-off-by: Markus Lidel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/i2o.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/i2o.h b/include/linux/i2o.h index 777a80f9f4a3..229bb91364ad 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -976,7 +976,7 @@ extern void i2o_debug_state(struct i2o_controller *c); #define I2O_TIMEOUT_MESSAGE_GET 5 #define I2O_TIMEOUT_RESET 30 #define I2O_TIMEOUT_STATUS_GET 5 -#define I2O_TIMEOUT_LCT_GET 20 +#define I2O_TIMEOUT_LCT_GET 360 #define I2O_TIMEOUT_SCSI_SCB_ABORT 240 /* retries */ -- cgit v1.2.3 From f89cf87cb3bf03fd238c5eb5bbc0c55ea919db06 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 16 Dec 2004 17:20:49 -0800 Subject: [PATCH] parenthesize init_wait() macro parameters Addresses bug #3863, from Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/wait.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/wait.h b/include/linux/wait.h index 8b3a2b86d92a..ddb0a16f31c9 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -326,8 +326,8 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); wait_queue_t name = { \ .task = current, \ .func = autoremove_wake_function, \ - .task_list = { .next = &name.task_list, \ - .prev = &name.task_list, \ + .task_list = { .next = &(name).task_list, \ + .prev = &(name).task_list, \ }, \ } @@ -338,15 +338,15 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); .task = current, \ .func = wake_bit_function, \ .task_list = \ - LIST_HEAD_INIT(name.wait.task_list), \ + LIST_HEAD_INIT((name).wait.task_list), \ }, \ } #define init_wait(wait) \ do { \ - wait->task = current; \ - wait->func = autoremove_wake_function; \ - INIT_LIST_HEAD(&wait->task_list); \ + (wait)->task = current; \ + (wait)->func = autoremove_wake_function; \ + INIT_LIST_HEAD(&(wait)->task_list); \ } while (0) /** -- cgit v1.2.3