📄 ohci-sl811-regs.h
字号:
/* * SL811HS OHCI HCD (Host Controller Driver) for USB. * * linux/drivers/usb/host/ohci-sl811-regs.h * * Copyright (C) 2004 by Lothar Wassmann <LW at KARO-electronics.de> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * * Macros for OHCI register access to facilitate OHCI emulation * on non-OHCI compliant hardware * */#include "ohci-sl811.h"#define hcd_to_sl811_dev(hcd) ((struct hc_sl811_dev *)dev_get_drvdata(hcd->self.controller))#define ohci_to_sl811_dev(ohci) ((struct hc_sl811_dev *)dev_get_drvdata(ohci->hcd.self.controller))#define hc_sl811_dev_to_hcd(dev) ((struct usb_hcd *)dev_get_drvdata(&dev->dev))/* * OHCI emulation */static void sl811_reset_hc(struct usb_hcd *hcd, int cold);static int hc_sl811_detect_device(struct usb_hcd *hcd);#define HcControl (regs->control)#define HcCmdStatus (regs->cmdstatus)#define HcFmRemaining (regs->fmremaining)#define HcFmNumber (regs->fmnumber)#define HcFmInterval (regs->fminterval)#define HcPeriodicStart (regs->periodicstart)#define HcLsThreshold (regs->lsthresh)#define HcRhStatus (regs->roothub.status)#define HcRhPortStatus (regs->roothub.portstatus)#define HcIntrEnable (regs->intrenable)#define HcIntrStatus (regs->intrstatus)#define HcControlHeadED (regs->ed_controlhead)#define HcBulkHeadED (regs->ed_bulkhead)#define HcDoneHead (regs->donehead)#define HcControlCurrentED (regs->ed_controlcurrent)#define HcBulkCurrentED (regs->ed_bulkcurrent)#define HcPeriodCurrentED (regs->ed_periodcurrent)#define HCCADoneHead (hcca->done_head)#define HCCAFrameNumber (hcca->frame_no)#define HCCAInterruptTable (hcca->int_table)static void sl811_trigger_bh(struct hc_sl811_dev *dev, dev_state_t req){ if (dev->dev_state & DEV_TRIGGERED) { dev->dev_state |= req; } else { dev->dev_state |= req; tasklet_schedule(&dev->usb_reset_bh); }}static void ohci_sl811_update_intrstatus(struct ohci_hcd *ohci, u32 set){ struct usb_hcd *hcd = ohci_to_hcd(ohci); struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); struct ohci_regs *regs = ohci->regs; unsigned long flags; u32 stat; u32 chg; local_irq_save(flags); stat = ohci->regs->intrstatus; chg = set & ~stat; if (chg) { HcIntrStatus |= set; ohci_vdbg(ohci, "%s: HcIntrStatus changed from %08x to %08x (%08x)\n", __FUNCTION__, stat, HcIntrStatus, set); if (HcIntrEnable & set) { WARN_ON(!in_interrupt()); sl811_trigger_bh(dev, DEV_OHCI_IRQ); } } local_irq_restore(flags);}static void ohci_sl811_update_portstatus(struct ohci_hcd *ohci, int port, u32 set, u32 clr){ unsigned long flags; u32 stat; u32 new; u32 chg; WARN_ON(!ohci->regs); if (!ohci->regs) { return; } if (port < 0 || port >= MAX_ROOT_PORTS) { printk("%s: Invalid port number %d\n", __FUNCTION__, port); return; } local_irq_save(flags); stat = ohci->regs->roothub.portstatus[port]; new = ((stat & 0xffff) | set) & ~clr; chg = (stat ^ new) & 0xffff; ohci_vdbg(ohci, "%s: old: %08x new: %08x chg: %04x\n", __FUNCTION__, stat, new, chg); if (chg) { u32 int_status = OHCI_INTR_RHSC; chg |= stat >> 16; ohci_vdbg(ohci, "%s: Changed port_status from %08x to: %08x\n", __FUNCTION__, ohci->regs->roothub.portstatus[port], new | (chg << 16)); ohci->regs->roothub.portstatus[port] = new | (chg << 16); if ((chg & RH_PS_CCS) && (ohci->regs->control & RH_HS_DRWE)) { int_status |= OHCI_INTR_RD; } ohci_sl811_update_intrstatus(ohci, int_status); } else { ohci_vdbg(ohci, "%s: Leaving portstatus unchanged: %08x\n", __FUNCTION__, ohci->regs->roothub.portstatus[port]); } local_irq_restore(flags);}/* * Software API */#define ohci_read_reg(hc,n) \({ \ u32 *reg = &(hc)->regs->n; \ u32 val; \ \ val = *reg; \ val; \})#define ohci_write_reg(hc,n,v) \{ \ u32 *reg = &ohci->regs->n; \ \ *reg = v; \}#define ohci_read_roothub_reg(hc,n) \ ohci_read_reg(hc,roothub.n)#define ohci_write_roothub_reg(hc,n,v) \({ \ u32 *reg = &ohci->regs->roothub.n; \ \ *reg = v; \})#define ohci_read_intrstatus(hc) ohci_read_reg(hc, intrstatus)#define ohci_read_intrenable(hc) ohci_read_reg(hc, intrenable)#define ohci_read_intrdisable ohci_read_intrenable#define ohci_read_control(hc) ohci_read_reg(hc, control)#define ohci_read_cmdstatus(hc) ohci_read_reg(hc, cmdstatus)#define ohci_read_roothub_a(hc) ohci_read_roothub_reg(hc, a)#define ohci_read_roothub_b(hc) ohci_read_roothub_reg(hc, b)#define ohci_read_roothub_status(hc) ohci_read_roothub_reg(hc, status)#define ohci_write_roothub_a(hc,v) ohci_write_roothub_reg(hc,a,v)#define ohci_write_roothub_b(hc,v) ohci_write_roothub_reg(hc,b,v)#define ohci_write_controlhead(hc,v) ohci_write_reg(hc, ed_controlhead, v)#define ohci_write_controlcurrent(hc,v) ohci_write_reg(hc, ed_controlcurrent, v)#define ohci_write_bulkhead(hc,v) ohci_write_reg(hc, ed_bulkhead, v)#define ohci_write_bulkcurrent(hc,v) ohci_write_reg(hc, ed_bulkcurrent, v)#define ohci_write_periodcurrent(hc,v) ohci_write_reg(hc, ed_periodcurrent, v)static void _ohci_write_control(struct ohci_regs *regs, struct usb_hcd *hcd, u32 ctrl){ unsigned long flags; struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); struct ohci_hcd *ohci = hcd_to_ohci(hcd); int old_state; int new_state = ctrl & OHCI_CTRL_HCFS; u32 chg; u32 old; local_irq_save(flags); old_state = regs->control & OHCI_CTRL_HCFS; old = regs->control; chg = old ^ ctrl; regs->control = ctrl; local_irq_restore(flags); if (old_state != new_state) { ohci_dbg(ohci, "%s: Changing state from %08x to %08x\n", __FUNCTION__, old_state, new_state); switch (new_state) { case OHCI_USB_RESET: ohci_dbg(ohci, "%s: Going to USB_RESET state: %12u\n", __FUNCTION__, OSCR); sl811_reset_hc(hcd, 0); break; case OHCI_USB_RESUME: ohci_dbg(ohci, "%s: Going to USB_RESUME state: %12u\n", __FUNCTION__, OSCR); if (old_state == OHCI_USB_SUSPEND) { ohci_dbg(ohci, "%s: Deactivating SUSPEND mode\n", __FUNCTION__); hc_sl811_set_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SOFENA); } break; case OHCI_USB_SUSPEND: ohci_dbg(ohci, "%s: Going to USB_SUSPEND state: %12u\n", __FUNCTION__, OSCR); if (old_state == OHCI_USB_OPER) { ohci_dbg(ohci, "%s: Activating SUSPEND mode\n", __FUNCTION__); hc_sl811_clr_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SOFENA); local_irq_save(flags); __hc_sl811_clr_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_SOFINTR); del_timer(&dev->int_timer); local_irq_restore(flags); } break; case OHCI_USB_OPER: ohci_dbg(ohci, "%s: Going to USB_OPERATIONAL state: %12u\n", __FUNCTION__, OSCR); switch (old_state) { case OHCI_USB_RESET: ohci_dbg(ohci, "%s: Leaving USB_RESET state: %12u\n", __FUNCTION__, OSCR); break; case OHCI_USB_RESUME: ohci_dbg(ohci, "%s: Disabling USB Resume Interrupt\n", __FUNCTION__); hc_sl811_clr_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); hc_sl811_clr_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_RESUME); ohci_dbg(ohci, "%s: int_mask=%02x, int_stat=%02x\n", __FUNCTION__, hc_sl811_read_reg(dev, SL11H_INTENBLREG), hc_sl811_read_reg(dev, SL11H_INTSTATREG)); break; case OHCI_USB_SUSPEND: ohci_dbg(ohci, "%s: Enabling USB Resume Interrupt\n", __FUNCTION__); local_irq_save(flags); __hc_sl811_set_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_RESUME); __hc_sl811_set_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); local_irq_restore(flags); break; } break; } }}static void ohci_write_control(struct ohci_hcd *ohci, u32 ctrl){ _ohci_write_control(ohci->regs, ohci_to_hcd(ohci), ctrl);}static void _ohci_write_cmdstatus(struct ohci_regs *regs, struct usb_hcd *hcd, u32 cmd){ unsigned long flags; u32 chg; if (cmd & OHCI_HCR) { sl811_reset_hc(hcd, 0); cmd &= ~OHCI_HCR; } local_irq_save(flags); chg = cmd ^ regs->cmdstatus; regs->cmdstatus = cmd; local_irq_restore(flags);}static void ohci_write_cmdstatus(struct ohci_hcd *ohci, u32 cmd){ _ohci_write_cmdstatus(ohci->regs, ohci_to_hcd(ohci), cmd);}static void ohci_write_roothub_status(struct ohci_hcd *ohci, u32 mask){ //struct usb_hcd *hcd = ohci_to_hcd(ohci); //struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); unsigned long flags; u32 *reg = &ohci->regs->roothub.status; u32 status = 0; local_irq_save(flags); if (mask & RH_HS_LPS) { // Power control not supported on SL811 } if (mask & RH_HS_DRWE) { status |= RH_HS_DRWE; } if (mask & RH_HS_CRWE) { status &= ~RH_HS_DRWE; } if (mask & RH_HS_LPSC) { // Power control not supported on SL811 status &= ~RH_HS_LPSC; } if (mask & RH_HS_OCIC) { status &= ~RH_HS_OCIC; } if (*reg != status) { ohci_sl811_update_intrstatus(ohci, OHCI_INTR_RHSC); } *reg = status; local_irq_restore(flags);}static u32 ohci_read_roothub_portstatus(struct ohci_hcd *ohci, int port){ struct ohci_roothub_regs *rh_regs = &ohci->regs->roothub; u32 status = RH_PS_PPS; static u32 old_status = -1; if (port < 0 || port >= MAX_ROOT_PORTS) { ohci_err(ohci, "%s: Invalid port number %d\n", __FUNCTION__, port); return -1; } status |= rh_regs->portstatus[port]; if (old_status != status) { ohci_vdbg(ohci, "%s: Read %08x from portstatus[%d]\n", __FUNCTION__, status, port); old_status = status; } return status;}static void ohci_write_roothub_portstatus(struct ohci_hcd *ohci, int port, u32 mask){ unsigned long flags; struct ohci_roothub_regs *rh_regs = &ohci->regs->roothub; u32 status; if (port < 0 || port >= MAX_ROOT_PORTS) { printk("%s: Invalid port number %d\n", __FUNCTION__, port); return; }#ifdef DEBUG status = rh_regs->portstatus[port]; ohci_vdbg(ohci, "%s: Writing %08x to portstatus[%d]: %08x\n", __FUNCTION__, mask, port, status);#endif local_irq_save(flags); status = rh_regs->portstatus[port]; if (mask & RH_PS_PESC) { status &= ~RH_PS_PESC; } if (mask & RH_PS_PSSC) { status &= ~RH_PS_PSSC; } if (mask & RH_PS_OCIC) { status &= ~RH_PS_OCIC; } if (mask & RH_PS_PRSC) { status &= ~RH_PS_PRSC; } if (mask & RH_PS_CSC) { status &= ~RH_PS_CSC; } if (status & RH_PS_CCS) { if (mask & RH_PS_CCS) { // clear PES, if device is removable if (rh_regs->b & (1 << port)) { status &= ~RH_PS_PES; } } if (mask & RH_PS_PES) { status |= RH_PS_PES; } if (mask & RH_PS_PSS) { status |= RH_PS_PSS; } if (mask & RH_PS_POCI) { status &= ~RH_PS_PSS; } if (mask & RH_PS_PRS) { status |= RH_PS_PRS; } if (mask & RH_PS_PPS) { // PPS enabled: set PPS } if (mask & RH_PS_LSDA) { // PPS enabled: clear PPS } } else { if (mask & (RH_PS_PRS | RH_PS_PES | RH_PS_PSS)) { status |= RH_PS_CSC; } } if (status != rh_regs->portstatus[port]) { ohci_vdbg(ohci, "%s: status of port %d changed from %08x to %08x\n", __FUNCTION__, port, rh_regs->portstatus[port], status); } rh_regs->portstatus[port] = status; if (status & RH_PS_PRS) { ohci_vdbg(ohci, "%s: Performing USB Port reset\n", __FUNCTION__); hc_sl811_usb_reset(ohci_to_hcd(ohci), -1); } local_irq_restore(flags);}static void ohci_write_intrstatus(struct ohci_hcd *ohci, u32 ohci_mask){ unsigned long flags; ohci_vdbg(ohci, "%s: Acknowledging Interrupts: %08x\n", __FUNCTION__, ohci_mask); local_irq_save(flags); ohci->regs->intrstatus &= ~ohci_mask; local_irq_restore(flags);}static void ohci_write_intrenable(struct ohci_hcd *ohci, u32 ohci_mask){ unsigned long flags; ohci_vdbg(ohci, "%s: Enabling OHCI Interrupts: %08x\n", __FUNCTION__, ohci_mask); local_irq_save(flags); ohci->regs->intrenable |= ohci_mask; local_irq_restore(flags);}static void ohci_write_intrdisable(struct ohci_hcd *ohci, u32 ohci_mask){ unsigned long flags; ohci_vdbg(ohci, "%s: Disabling OHCI Interrupts: %08x\n", __FUNCTION__, ohci_mask); local_irq_save(flags); ohci->regs->intrenable &= ~ohci_mask; local_irq_restore(flags);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -