aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acorn/char/i2c.c1
-rw-r--r--drivers/acpi/i2c_ec.c2
-rw-r--r--drivers/acpi/osl.c2
-rw-r--r--drivers/ata/Kconfig3
-rw-r--r--drivers/ata/ata_generic.c2
-rw-r--r--drivers/ata/ata_piix.c10
-rw-r--r--drivers/ata/libata-core.c5
-rw-r--r--drivers/ata/libata-eh.c6
-rw-r--r--drivers/ata/libata-sff.c6
-rw-r--r--drivers/ata/pata_ali.c8
-rw-r--r--drivers/ata/pata_amd.c37
-rw-r--r--drivers/ata/pata_artop.c22
-rw-r--r--drivers/ata/pata_atiixp.c12
-rw-r--r--drivers/ata/pata_cmd64x.c6
-rw-r--r--drivers/ata/pata_cs5520.c2
-rw-r--r--drivers/ata/pata_cs5530.c2
-rw-r--r--drivers/ata/pata_cs5535.c2
-rw-r--r--drivers/ata/pata_cypress.c2
-rw-r--r--drivers/ata/pata_efar.c12
-rw-r--r--drivers/ata/pata_hpt366.c2
-rw-r--r--drivers/ata/pata_hpt37x.c8
-rw-r--r--drivers/ata/pata_hpt3x2n.c2
-rw-r--r--drivers/ata/pata_hpt3x3.c2
-rw-r--r--drivers/ata/pata_isapnp.c2
-rw-r--r--drivers/ata/pata_it821x.c4
-rw-r--r--drivers/ata/pata_jmicron.c5
-rw-r--r--drivers/ata/pata_legacy.c14
-rw-r--r--drivers/ata/pata_mpiix.c11
-rw-r--r--drivers/ata/pata_netcell.c3
-rw-r--r--drivers/ata/pata_ns87410.c9
-rw-r--r--drivers/ata/pata_oldpiix.c9
-rw-r--r--drivers/ata/pata_opti.c12
-rw-r--r--drivers/ata/pata_optidma.c14
-rw-r--r--drivers/ata/pata_pcmcia.c2
-rw-r--r--drivers/ata/pata_pdc2027x.c8
-rw-r--r--drivers/ata/pata_qdi.c4
-rw-r--r--drivers/ata/pata_radisys.c2
-rw-r--r--drivers/ata/pata_rz1000.c2
-rw-r--r--drivers/ata/pata_sc1200.c2
-rw-r--r--drivers/ata/pata_serverworks.c12
-rw-r--r--drivers/ata/pata_sil680.c2
-rw-r--r--drivers/ata/pata_sis.c20
-rw-r--r--drivers/ata/pata_sl82c105.c11
-rw-r--r--drivers/ata/pata_triflex.c15
-rw-r--r--drivers/ata/pata_via.c13
-rw-r--r--drivers/ata/sata_mv.c1
-rw-r--r--drivers/ata/sata_nv.c6
-rw-r--r--drivers/ata/sata_sis.c6
-rw-r--r--drivers/ata/sata_uli.c6
-rw-r--r--drivers/ata/sata_via.c7
-rw-r--r--drivers/base/base.h2
-rw-r--r--drivers/base/bus.c133
-rw-r--r--drivers/base/class.c42
-rw-r--r--drivers/base/core.c230
-rw-r--r--drivers/base/dd.c147
-rw-r--r--drivers/base/driver.c16
-rw-r--r--drivers/base/firmware_class.c14
-rw-r--r--drivers/base/node.c13
-rw-r--r--drivers/base/platform.c30
-rw-r--r--drivers/base/power/resume.c37
-rw-r--r--drivers/base/power/suspend.c92
-rw-r--r--drivers/base/power/sysfs.c35
-rw-r--r--drivers/block/loop.c87
-rw-r--r--drivers/block/ub.c38
-rw-r--r--drivers/bluetooth/bfusb.c316
-rw-r--r--drivers/bluetooth/hci_ldisc.c13
-rw-r--r--drivers/bluetooth/hci_usb.c3
-rw-r--r--drivers/bluetooth/hci_vhci.c99
-rw-r--r--drivers/char/Kconfig18
-rw-r--r--drivers/char/Makefile2
-rw-r--r--drivers/char/generic_serial.c11
-rw-r--r--drivers/char/hpet.c4
-rw-r--r--drivers/char/hw_random/intel-rng.c186
-rw-r--r--drivers/char/istallion.c20
-rw-r--r--drivers/char/lp.c4
-rw-r--r--drivers/char/mem.c47
-rw-r--r--drivers/char/mspec.c421
-rw-r--r--drivers/char/pc8736x_gpio.c10
-rw-r--r--drivers/char/raw.c19
-rw-r--r--drivers/char/rtc.c4
-rw-r--r--drivers/char/s3c2410-rtc.c591
-rw-r--r--drivers/char/scx200_gpio.c4
-rw-r--r--drivers/char/selection.c2
-rw-r--r--drivers/char/specialix.c16
-rw-r--r--drivers/char/sysrq.c2
-rw-r--r--drivers/char/tty_io.c104
-rw-r--r--drivers/char/tty_ioctl.c35
-rw-r--r--drivers/char/vc_screen.c5
-rw-r--r--drivers/char/vt.c104
-rw-r--r--drivers/char/vt_ioctl.c17
-rw-r--r--drivers/char/watchdog/Kconfig15
-rw-r--r--drivers/char/watchdog/Makefile1
-rw-r--r--drivers/char/watchdog/omap_wdt.c391
-rw-r--r--drivers/char/watchdog/omap_wdt.h64
-rw-r--r--drivers/char/watchdog/shwdt.c110
-rw-r--r--drivers/eisa/eisa-bus.c23
-rw-r--r--drivers/firmware/dmi_scan.c23
-rw-r--r--drivers/hwmon/Kconfig51
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/abituguru.c30
-rw-r--r--drivers/hwmon/adm1021.c31
-rw-r--r--drivers/hwmon/adm1025.c94
-rw-r--r--drivers/hwmon/adm1026.c286
-rw-r--r--drivers/hwmon/adm1031.c114
-rw-r--r--drivers/hwmon/adm9240.c105
-rw-r--r--drivers/hwmon/asb100.c122
-rw-r--r--drivers/hwmon/atxp1.c28
-rw-r--r--drivers/hwmon/ds1621.c28
-rw-r--r--drivers/hwmon/f71805f.c336
-rw-r--r--drivers/hwmon/fscher.c106
-rw-r--r--drivers/hwmon/fscpos.c75
-rw-r--r--drivers/hwmon/gl518sm.c74
-rw-r--r--drivers/hwmon/gl520sm.c128
-rw-r--r--drivers/hwmon/hdaps.c6
-rw-r--r--drivers/hwmon/it87.c470
-rw-r--r--drivers/hwmon/k8temp.c294
-rw-r--r--drivers/hwmon/lm63.c96
-rw-r--r--drivers/hwmon/lm75.c24
-rw-r--r--drivers/hwmon/lm77.c33
-rw-r--r--drivers/hwmon/lm78.c89
-rw-r--r--drivers/hwmon/lm80.c85
-rw-r--r--drivers/hwmon/lm83.c128
-rw-r--r--drivers/hwmon/lm85.c173
-rw-r--r--drivers/hwmon/lm87.c191
-rw-r--r--drivers/hwmon/lm90.c90
-rw-r--r--drivers/hwmon/lm92.c34
-rw-r--r--drivers/hwmon/max1619.c33
-rw-r--r--drivers/hwmon/pc87360.c231
-rw-r--r--drivers/hwmon/sis5595.c102
-rw-r--r--drivers/hwmon/smsc47b397.c40
-rw-r--r--drivers/hwmon/smsc47m1.c82
-rw-r--r--drivers/hwmon/smsc47m192.c150
-rw-r--r--drivers/hwmon/via686a.c84
-rw-r--r--drivers/hwmon/vt1211.c1355
-rw-r--r--drivers/hwmon/vt8231.c187
-rw-r--r--drivers/hwmon/w83627ehf.c486
-rw-r--r--drivers/hwmon/w83627hf.c233
-rw-r--r--drivers/hwmon/w83781d.c278
-rw-r--r--drivers/hwmon/w83791d.c7
-rw-r--r--drivers/hwmon/w83792d.c554
-rw-r--r--drivers/hwmon/w83l785ts.c28
-rw-r--r--drivers/i2c/Kconfig2
-rw-r--r--drivers/i2c/algos/Kconfig6
-rw-r--r--drivers/i2c/algos/Makefile1
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c23
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-sgi.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-sibyte.c215
-rw-r--r--drivers/i2c/busses/Kconfig36
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c2
-rw-r--r--drivers/i2c/busses/i2c-ali1563.c2
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c2
-rw-r--r--drivers/i2c/busses/i2c-amd756.c2
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c2
-rw-r--r--drivers/i2c/busses/i2c-au1550.c21
-rw-r--r--drivers/i2c/busses/i2c-elektor.c1
-rw-r--r--drivers/i2c/busses/i2c-hydra.c1
-rw-r--r--drivers/i2c/busses/i2c-i801.c2
-rw-r--r--drivers/i2c/busses/i2c-i810.c2
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c2
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c18
-rw-r--r--drivers/i2c/busses/i2c-isa.c42
-rw-r--r--drivers/i2c/busses/i2c-ixp2000.c1
-rw-r--r--drivers/i2c/busses/i2c-ixp4xx.c1
-rw-r--r--drivers/i2c/busses/i2c-mpc.c2
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c2
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c2
-rw-r--r--drivers/i2c/busses/i2c-ocores.c2
-rw-r--r--drivers/i2c/busses/i2c-omap.c676
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c1
-rw-r--r--drivers/i2c/busses/i2c-parport.c1
-rw-r--r--drivers/i2c/busses/i2c-piix4.c2
-rw-r--r--drivers/i2c/busses/i2c-powermac.c2
-rw-r--r--drivers/i2c/busses/i2c-prosavage.c1
-rw-r--r--drivers/i2c/busses/i2c-pxa.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c2
-rw-r--r--drivers/i2c/busses/i2c-savage4.c1
-rw-r--r--drivers/i2c/busses/i2c-sibyte.c160
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c2
-rw-r--r--drivers/i2c/busses/i2c-sis630.c2
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c2
-rw-r--r--drivers/i2c/busses/i2c-stub.c21
-rw-r--r--drivers/i2c/busses/i2c-via.c1
-rw-r--r--drivers/i2c/busses/i2c-viapro.c10
-rw-r--r--drivers/i2c/busses/i2c-voodoo3.c2
-rw-r--r--drivers/i2c/busses/scx200_acb.c2
-rw-r--r--drivers/i2c/busses/scx200_i2c.c12
-rw-r--r--drivers/i2c/chips/eeprom.c8
-rw-r--r--drivers/i2c/chips/isp1301_omap.c2
-rw-r--r--drivers/i2c/chips/max6875.c25
-rw-r--r--drivers/i2c/chips/pca9539.c11
-rw-r--r--drivers/i2c/chips/pcf8574.c22
-rw-r--r--drivers/i2c/chips/pcf8591.c58
-rw-r--r--drivers/i2c/chips/tps65010.c2
-rw-r--r--drivers/i2c/i2c-core.c72
-rw-r--r--drivers/i2c/i2c-dev.c109
-rw-r--r--drivers/ide/ide.c6
-rw-r--r--drivers/ide/mips/au1xxx-ide.c4
-rw-r--r--drivers/ide/pci/atiixp.c21
-rw-r--r--drivers/ide/ppc/pmac.c14
-rw-r--r--drivers/ieee1394/Kconfig11
-rw-r--r--drivers/ieee1394/csr.c31
-rw-r--r--drivers/ieee1394/csr.h109
-rw-r--r--drivers/ieee1394/dma.c7
-rw-r--r--drivers/ieee1394/dma.h90
-rw-r--r--drivers/ieee1394/dv1394-private.h6
-rw-r--r--drivers/ieee1394/dv1394.c47
-rw-r--r--drivers/ieee1394/eth1394.c12
-rw-r--r--drivers/ieee1394/highlevel.h201
-rw-r--r--drivers/ieee1394/hosts.c23
-rw-r--r--drivers/ieee1394/hosts.h51
-rw-r--r--drivers/ieee1394/ieee1394-ioctl.h9
-rw-r--r--drivers/ieee1394/ieee1394.h316
-rw-r--r--drivers/ieee1394/ieee1394_core.c9
-rw-r--r--drivers/ieee1394/ieee1394_core.h28
-rw-r--r--drivers/ieee1394/ieee1394_hotplug.h30
-rw-r--r--drivers/ieee1394/ieee1394_transactions.c114
-rw-r--r--drivers/ieee1394/ieee1394_transactions.h41
-rw-r--r--drivers/ieee1394/ieee1394_types.h68
-rw-r--r--drivers/ieee1394/iso.c5
-rw-r--r--drivers/ieee1394/iso.h87
-rw-r--r--drivers/ieee1394/nodemgr.c227
-rw-r--r--drivers/ieee1394/nodemgr.h27
-rw-r--r--drivers/ieee1394/ohci1394.c73
-rw-r--r--drivers/ieee1394/pcilynx.c1
-rw-r--r--drivers/ieee1394/raw1394-private.h3
-rw-r--r--drivers/ieee1394/raw1394.c138
-rw-r--r--drivers/ieee1394/sbp2.c481
-rw-r--r--drivers/ieee1394/sbp2.h37
-rw-r--r--drivers/ieee1394/video1394.c52
-rw-r--r--drivers/infiniband/core/addr.c4
-rw-r--r--drivers/infiniband/core/mad.c5
-rw-r--r--drivers/infiniband/hw/amso1100/c2_ae.c2
-rw-r--r--drivers/infiniband/hw/amso1100/c2_alloc.c2
-rw-r--r--drivers/infiniband/hw/amso1100/c2_cm.c15
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c8
-rw-r--r--drivers/infiniband/hw/amso1100/c2_rnic.c4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_common.h54
-rw-r--r--drivers/infiniband/hw/ipath/ipath_cq.c48
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c359
-rw-r--r--drivers/infiniband/hw/ipath/ipath_eeprom.c17
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c974
-rw-r--r--drivers/infiniband/hw/ipath/ipath_fs.c22
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6110.c137
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6120.c263
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c56
-rw-r--r--drivers/infiniband/hw/ipath/ipath_intr.c280
-rw-r--r--drivers/infiniband/hw/ipath/ipath_kernel.h116
-rw-r--r--drivers/infiniband/hw/ipath/ipath_keys.c12
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mad.c16
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mr.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_qp.c16
-rw-r--r--drivers/infiniband/hw/ipath/ipath_rc.c77
-rw-r--r--drivers/infiniband/hw/ipath/ipath_registers.h40
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ruc.c14
-rw-r--r--drivers/infiniband/hw/ipath/ipath_srq.c23
-rw-r--r--drivers/infiniband/hw/ipath/ipath_sysfs.c21
-rw-r--r--drivers/infiniband/hw/ipath/ipath_uc.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ud.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_user_pages.c56
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c43
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.h18
-rw-r--r--drivers/infiniband/hw/ipath/ipath_wc_ppc64.c20
-rw-r--r--drivers/infiniband/hw/ipath/ipath_wc_x86_64.c13
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_fs.c4
-rw-r--r--drivers/infiniband/ulp/iser/Kconfig13
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c2
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h9
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c60
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c42
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c8
-rw-r--r--drivers/input/keyboard/Kconfig9
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/omap-keypad.c492
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c14
-rw-r--r--drivers/isdn/capi/capifs.c2
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c2
-rw-r--r--drivers/isdn/hisax/hfc_usb.h6
-rw-r--r--drivers/isdn/i4l/isdn_net.c4
-rw-r--r--drivers/leds/leds-net48xx.c9
-rw-r--r--drivers/macintosh/via-pmu-backlight.c2
-rw-r--r--drivers/macintosh/windfarm_smu_sat.c7
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c2
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-urb.c1
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c2
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c1
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c1
-rw-r--r--drivers/media/video/cx88/cx88-vp3054-i2c.c1
-rw-r--r--drivers/media/video/ov511.c7
-rw-r--r--drivers/media/video/pwc/pwc-if.c2
-rw-r--r--drivers/media/video/w9968cf.c7
-rw-r--r--drivers/media/video/zoran_card.c1
-rw-r--r--drivers/mfd/ucb1x00-ts.c45
-rw-r--r--drivers/misc/ibmasm/ibmasmfs.c17
-rw-r--r--drivers/mmc/at91_mci.c5
-rw-r--r--drivers/mmc/au1xmmc.c2
-rw-r--r--drivers/mmc/imxmmc.c2
-rw-r--r--drivers/mmc/mmc.c1
-rw-r--r--drivers/mmc/mmc_block.c66
-rw-r--r--drivers/mmc/mmci.c13
-rw-r--r--drivers/mmc/omap.c7
-rw-r--r--drivers/mmc/sdhci.c2
-rw-r--r--drivers/mmc/wbsd.c2
-rw-r--r--drivers/net/3c509.c1
-rw-r--r--drivers/net/3c59x.c1
-rw-r--r--drivers/net/8390.c6
-rw-r--r--drivers/net/Kconfig5
-rw-r--r--drivers/net/acenic.c4
-rw-r--r--drivers/net/appletalk/ipddp.c5
-rw-r--r--drivers/net/arm/at91_ether.c2
-rw-r--r--drivers/net/bnx2.c32
-rw-r--r--drivers/net/bonding/bond_3ad.c70
-rw-r--r--drivers/net/bonding/bond_main.c230
-rw-r--r--drivers/net/bonding/bond_sysfs.c56
-rw-r--r--drivers/net/bonding/bonding.h34
-rw-r--r--drivers/net/e1000/e1000_hw.h4
-rw-r--r--drivers/net/fec_8xx/fec_main.c2
-rw-r--r--drivers/net/forcedeth.c8
-rw-r--r--drivers/net/fs_enet/fs_enet.h3
-rw-r--r--drivers/net/gt64240eth.h402
-rw-r--r--drivers/net/irda/Kconfig1
-rw-r--r--drivers/net/irda/irda-usb.c18
-rw-r--r--drivers/net/irda/nsc-ircc.c2
-rw-r--r--drivers/net/irda/smsc-ircc2.c38
-rw-r--r--drivers/net/irda/stir4200.c15
-rw-r--r--drivers/net/irda/via-ircc.c7
-rw-r--r--drivers/net/irda/vlsi_ir.h2
-rw-r--r--drivers/net/loopback.c31
-rw-r--r--drivers/net/ne3210.c1
-rw-r--r--drivers/net/phy/phy_device.c2
-rw-r--r--drivers/net/pppoe.c1
-rw-r--r--drivers/net/s2io.c10
-rw-r--r--drivers/net/skge.c357
-rw-r--r--drivers/net/skge.h37
-rw-r--r--drivers/net/smc91x.h19
-rw-r--r--drivers/net/stnic.c2
-rw-r--r--drivers/net/tg3.c480
-rw-r--r--drivers/net/tg3.h48
-rw-r--r--drivers/net/tokenring/lanstreamer.c59
-rw-r--r--drivers/net/tokenring/lanstreamer.h12
-rw-r--r--drivers/net/tulip/de4x5.c1
-rw-r--r--drivers/net/typhoon.c4
-rw-r--r--drivers/net/wan/Kconfig12
-rw-r--r--drivers/net/wan/Makefile19
-rw-r--r--drivers/net/wan/hdlc.c (renamed from drivers/net/wan/hdlc_generic.c)169
-rw-r--r--drivers/net/wan/hdlc_cisco.c200
-rw-r--r--drivers/net/wan/hdlc_fr.c389
-rw-r--r--drivers/net/wan/hdlc_ppp.c77
-rw-r--r--drivers/net/wan/hdlc_raw.c50
-rw-r--r--drivers/net/wan/hdlc_raw_eth.c49
-rw-r--r--drivers/net/wan/hdlc_x25.c54
-rw-r--r--drivers/net/wan/pc300.h1
-rw-r--r--drivers/net/wan/pc300_drv.c30
-rw-r--r--drivers/net/wan/syncppp.c4
-rw-r--r--drivers/net/wireless/airo.c19
-rw-r--r--drivers/net/wireless/atmel.c18
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx.h1
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c2
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c10
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_phy.c15
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_wx.c2
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_xmit.c5
-rw-r--r--drivers/net/wireless/hostap/hostap_ioctl.c10
-rw-r--r--drivers/net/wireless/ipw2100.c14
-rw-r--r--drivers/net/wireless/ipw2200.c16
-rw-r--r--drivers/net/wireless/orinoco.c10
-rw-r--r--drivers/net/wireless/prism54/isl_ioctl.c16
-rw-r--r--drivers/net/wireless/ray_cs.c2
-rw-r--r--drivers/net/wireless/strip.c4
-rw-r--r--drivers/net/wireless/wl3501_cs.c6
-rw-r--r--drivers/net/wireless/zd1201.c6
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.c137
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.h23
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c50
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.h5
-rw-r--r--drivers/net/wireless/zd1211rw/zd_netdev.c2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c10
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.h2
-rw-r--r--drivers/oprofile/oprofilefs.c11
-rw-r--r--drivers/parport/parport_serial.c4
-rw-r--r--drivers/pci/Kconfig25
-rw-r--r--drivers/pci/bus.c22
-rw-r--r--drivers/pci/hotplug/acpiphp.h5
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c127
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c4
-rw-r--r--drivers/pci/hotplug/cpqphp_sysfs.c2
-rw-r--r--drivers/pci/hotplug/fakephp.c18
-rw-r--r--drivers/pci/hotplug/pci_hotplug.h4
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c157
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c12
-rw-r--r--drivers/pci/hotplug/pcihp_skeleton.c9
-rw-r--r--drivers/pci/hotplug/shpchp.h2
-rw-r--r--drivers/pci/hotplug/shpchp_core.c6
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c4
-rw-r--r--drivers/pci/msi.c64
-rw-r--r--drivers/pci/pci-driver.c47
-rw-r--r--drivers/pci/pci-sysfs.c153
-rw-r--r--drivers/pci/pci.c59
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/pcie/Kconfig1
-rw-r--r--drivers/pci/pcie/Makefile3
-rw-r--r--drivers/pci/pcie/aer/Kconfig12
-rw-r--r--drivers/pci/pcie/aer/Makefile8
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c346
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h125
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c68
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c758
-rw-r--r--drivers/pci/pcie/aer/aerdrv_errprint.c248
-rw-r--r--drivers/pci/pcie/portdrv.h2
-rw-r--r--drivers/pci/pcie/portdrv_bus.c1
-rw-r--r--drivers/pci/pcie/portdrv_core.c11
-rw-r--r--drivers/pci/pcie/portdrv_pci.c211
-rw-r--r--drivers/pci/probe.c16
-rw-r--r--drivers/pci/quirks.c104
-rw-r--r--drivers/pci/remove.c37
-rw-r--r--drivers/pci/setup-bus.c13
-rw-r--r--drivers/rtc/Kconfig12
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-rs5c348.c9
-rw-r--r--drivers/rtc/rtc-sh.c467
-rw-r--r--drivers/s390/block/dasd_diag.c34
-rw-r--r--drivers/s390/block/xpram.c54
-rw-r--r--drivers/s390/char/fs3270.c1
-rw-r--r--drivers/s390/char/sclp.c31
-rw-r--r--drivers/s390/char/tty3270.c1
-rw-r--r--drivers/s390/char/vmwatchdog.c52
-rw-r--r--drivers/s390/cio/device_id.c38
-rw-r--r--drivers/s390/cio/ioasm.h220
-rw-r--r--drivers/s390/cio/qdio.h192
-rw-r--r--drivers/s390/net/iucv.c39
-rw-r--r--drivers/s390/net/qeth_main.c2
-rw-r--r--drivers/s390/s390mach.c93
-rw-r--r--drivers/scsi/aha1740.c1
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c3
-rw-r--r--drivers/scsi/mesh.c15
-rw-r--r--drivers/scsi/sim710.c1
-rw-r--r--drivers/serial/Kconfig11
-rw-r--r--drivers/serial/at91_serial.c2
-rw-r--r--drivers/serial/serial_core.c14
-rw-r--r--drivers/serial/sh-sci.c1146
-rw-r--r--drivers/serial/sh-sci.h90
-rw-r--r--drivers/serial/sunzilog.c2
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/Makefile12
-rw-r--r--drivers/usb/atm/ueagle-atm.c74
-rw-r--r--drivers/usb/class/usblp.c15
-rw-r--r--drivers/usb/core/Makefile2
-rw-r--r--drivers/usb/core/buffer.c4
-rw-r--r--drivers/usb/core/config.c4
-rw-r--r--drivers/usb/core/devices.c6
-rw-r--r--drivers/usb/core/devio.c68
-rw-r--r--drivers/usb/core/driver.c1012
-rw-r--r--drivers/usb/core/endpoint.c30
-rw-r--r--drivers/usb/core/file.c2
-rw-r--r--drivers/usb/core/generic.c208
-rw-r--r--drivers/usb/core/hcd-pci.c18
-rw-r--r--drivers/usb/core/hcd.c250
-rw-r--r--drivers/usb/core/hcd.h60
-rw-r--r--drivers/usb/core/hub.c555
-rw-r--r--drivers/usb/core/hub.h3
-rw-r--r--drivers/usb/core/inode.c13
-rw-r--r--drivers/usb/core/message.c148
-rw-r--r--drivers/usb/core/notify.c3
-rw-r--r--drivers/usb/core/sysfs.c60
-rw-r--r--drivers/usb/core/urb.c15
-rw-r--r--drivers/usb/core/usb.c569
-rw-r--r--drivers/usb/core/usb.h93
-rw-r--r--drivers/usb/gadget/Kconfig16
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/at91_udc.c8
-rw-r--r--drivers/usb/gadget/dummy_hcd.c41
-rw-r--r--drivers/usb/gadget/ether.c9
-rw-r--r--drivers/usb/gadget/file_storage.c35
-rw-r--r--drivers/usb/gadget/gmidi.c1337
-rw-r--r--drivers/usb/gadget/inode.c67
-rw-r--r--drivers/usb/gadget/net2280.c156
-rw-r--r--drivers/usb/gadget/omap_udc.c4
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c70
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.h24
-rw-r--r--drivers/usb/gadget/serial.c3
-rw-r--r--drivers/usb/host/Kconfig29
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-au1xxx.c2
-rw-r--r--drivers/usb/host/ehci-dbg.c31
-rw-r--r--drivers/usb/host/ehci-fsl.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c95
-rw-r--r--drivers/usb/host/ehci-hub.c14
-rw-r--r--drivers/usb/host/ehci-mem.c14
-rw-r--r--drivers/usb/host/ehci-pci.c10
-rw-r--r--drivers/usb/host/ehci-q.c26
-rw-r--r--drivers/usb/host/ehci-sched.c26
-rw-r--r--drivers/usb/host/ehci.h59
-rw-r--r--drivers/usb/host/isp116x-hcd.c4
-rw-r--r--drivers/usb/host/isp116x.h2
-rw-r--r--drivers/usb/host/ohci-at91.c7
-rw-r--r--drivers/usb/host/ohci-au1xxx.c7
-rw-r--r--drivers/usb/host/ohci-dbg.c18
-rw-r--r--drivers/usb/host/ohci-ep93xx.c3
-rw-r--r--drivers/usb/host/ohci-hcd.c64
-rw-r--r--drivers/usb/host/ohci-hub.c273
-rw-r--r--drivers/usb/host/ohci-lh7a404.c7
-rw-r--r--drivers/usb/host/ohci-mem.c1
-rw-r--r--drivers/usb/host/ohci-omap.c120
-rw-r--r--drivers/usb/host/ohci-pci.c16
-rw-r--r--drivers/usb/host/ohci-pnx4008.c476
-rw-r--r--drivers/usb/host/ohci-ppc-soc.c3
-rw-r--r--drivers/usb/host/ohci-pxa27x.c3
-rw-r--r--drivers/usb/host/ohci-s3c2410.c5
-rw-r--r--drivers/usb/host/ohci-sa1111.c5
-rw-r--r--drivers/usb/host/ohci.h5
-rw-r--r--drivers/usb/host/sl811-hcd.c13
-rw-r--r--drivers/usb/host/u132-hcd.c3295
-rw-r--r--drivers/usb/host/uhci-debug.c6
-rw-r--r--drivers/usb/host/uhci-hcd.c12
-rw-r--r--drivers/usb/host/uhci-hub.c12
-rw-r--r--drivers/usb/image/mdc800.c4
-rw-r--r--drivers/usb/image/microtek.c18
-rw-r--r--drivers/usb/image/microtek.h4
-rw-r--r--drivers/usb/input/Kconfig30
-rw-r--r--drivers/usb/input/Makefile2
-rw-r--r--drivers/usb/input/acecad.c5
-rw-r--r--drivers/usb/input/appletouch.c5
-rw-r--r--drivers/usb/input/ati_remote.c8
-rw-r--r--drivers/usb/input/hid-core.c28
-rw-r--r--drivers/usb/input/hiddev.c2
-rw-r--r--drivers/usb/input/itmtouch.c2
-rw-r--r--drivers/usb/input/keyspan_remote.c3
-rw-r--r--drivers/usb/input/mtouchusb.c2
-rw-r--r--drivers/usb/input/powermate.c4
-rw-r--r--drivers/usb/input/touchkitusb.c2
-rw-r--r--drivers/usb/input/trancevibrator.c159
-rw-r--r--drivers/usb/input/usbmouse.c2
-rw-r--r--drivers/usb/input/usbtouchscreen.c285
-rw-r--r--drivers/usb/input/wacom.c1003
-rw-r--r--drivers/usb/input/wacom.h132
-rw-r--r--drivers/usb/input/wacom_sys.c315
-rw-r--r--drivers/usb/input/wacom_wac.c646
-rw-r--r--drivers/usb/input/wacom_wac.h48
-rw-r--r--drivers/usb/input/yealink.c2
-rw-r--r--drivers/usb/misc/Kconfig61
-rw-r--r--drivers/usb/misc/Makefile5
-rw-r--r--drivers/usb/misc/adutux.c900
-rw-r--r--drivers/usb/misc/auerswald.c6
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c19
-rw-r--r--drivers/usb/misc/cytherm.c35
-rw-r--r--drivers/usb/misc/ftdi-elan.c2809
-rw-r--r--drivers/usb/misc/idmouse.c2
-rw-r--r--drivers/usb/misc/ldusb.c10
-rw-r--r--drivers/usb/misc/legousbtower.c2
-rw-r--r--drivers/usb/misc/phidget.c43
-rw-r--r--drivers/usb/misc/phidget.h12
-rw-r--r--drivers/usb/misc/phidgetkit.c372
-rw-r--r--drivers/usb/misc/phidgetmotorcontrol.c466
-rw-r--r--drivers/usb/misc/phidgetservo.c117
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c2
-rw-r--r--drivers/usb/misc/usb_u132.h97
-rw-r--r--drivers/usb/misc/usblcd.c10
-rw-r--r--drivers/usb/misc/usbled.c20
-rw-r--r--drivers/usb/mon/mon_main.c7
-rw-r--r--drivers/usb/mon/mon_stat.c4
-rw-r--r--drivers/usb/mon/mon_text.c19
-rw-r--r--drivers/usb/mon/usb_mon.h5
-rw-r--r--drivers/usb/net/asix.c1005
-rw-r--r--drivers/usb/net/kaweth.c1
-rw-r--r--drivers/usb/net/net1080.c15
-rw-r--r--drivers/usb/net/pegasus.c20
-rw-r--r--drivers/usb/net/rtl8150.c2
-rw-r--r--drivers/usb/net/usbnet.c44
-rw-r--r--drivers/usb/net/usbnet.h1
-rw-r--r--drivers/usb/serial/Kconfig25
-rw-r--r--drivers/usb/serial/Makefile2
-rw-r--r--drivers/usb/serial/aircable.c625
-rw-r--r--drivers/usb/serial/airprime.c261
-rw-r--r--drivers/usb/serial/ark3116.c233
-rw-r--r--drivers/usb/serial/cyberjack.c6
-rw-r--r--drivers/usb/serial/cypress_m8.c129
-rw-r--r--drivers/usb/serial/ftdi_sio.c34
-rw-r--r--drivers/usb/serial/ftdi_sio.h10
-rw-r--r--drivers/usb/serial/garmin_gps.c219
-rw-r--r--drivers/usb/serial/generic.c6
-rw-r--r--drivers/usb/serial/ipaq.c39
-rw-r--r--drivers/usb/serial/ipw.c6
-rw-r--r--drivers/usb/serial/ir-usb.c6
-rw-r--r--drivers/usb/serial/keyspan_pda.c6
-rw-r--r--drivers/usb/serial/mos7840.c2962
-rw-r--r--drivers/usb/serial/omninet.c6
-rw-r--r--drivers/usb/serial/pl2303.c828
-rw-r--r--drivers/usb/serial/pl2303.h8
-rw-r--r--drivers/usb/serial/safe_serial.c6
-rw-r--r--drivers/usb/serial/usb-serial.c28
-rw-r--r--drivers/usb/storage/Kconfig12
-rw-r--r--drivers/usb/storage/Makefile1
-rw-r--r--drivers/usb/storage/initializers.c73
-rw-r--r--drivers/usb/storage/initializers.h1
-rw-r--r--drivers/usb/storage/karma.c155
-rw-r--r--drivers/usb/storage/karma.h7
-rw-r--r--drivers/usb/storage/libusual.c10
-rw-r--r--drivers/usb/storage/onetouch.c8
-rw-r--r--drivers/usb/storage/scsiglue.c15
-rw-r--r--drivers/usb/storage/transport.c5
-rw-r--r--drivers/usb/storage/unusual_devs.h32
-rw-r--r--drivers/usb/storage/usb.c11
-rw-r--r--drivers/usb/usb-skeleton.c101
-rw-r--r--drivers/video/Kconfig26
-rw-r--r--drivers/video/aty/radeon_pm.c15
-rw-r--r--drivers/video/backlight/hp680_bl.c4
-rw-r--r--drivers/video/backlight/locomolcd.c30
-rw-r--r--drivers/video/console/Kconfig2
-rw-r--r--drivers/video/hitfb.c229
-rw-r--r--drivers/video/i810/i810-i2c.c1
-rw-r--r--drivers/video/i810/i810_main.c12
-rw-r--r--drivers/video/intelfb/Makefile4
-rw-r--r--drivers/video/intelfb/intelfb.h79
-rw-r--r--drivers/video/intelfb/intelfb_i2c.c200
-rw-r--r--drivers/video/intelfb/intelfbdrv.c64
-rw-r--r--drivers/video/intelfb/intelfbhw.c136
-rw-r--r--drivers/video/intelfb/intelfbhw.h25
-rw-r--r--drivers/video/matrox/i2c-matroxfb.c12
-rw-r--r--drivers/video/nvidia/nvidia.c13
-rw-r--r--drivers/video/pvr2fb.c22
-rw-r--r--drivers/video/savage/savagefb-i2c.c1
-rw-r--r--drivers/video/savage/savagefb_driver.c14
624 files changed, 39641 insertions, 13035 deletions
diff --git a/drivers/acorn/char/i2c.c b/drivers/acorn/char/i2c.c
index c26c08b3682..bdb9c8b78ed 100644
--- a/drivers/acorn/char/i2c.c
+++ b/drivers/acorn/char/i2c.c
@@ -308,7 +308,6 @@ static struct i2c_algo_bit_data ioc_data = {
.getsda = ioc_getsda,
.getscl = ioc_getscl,
.udelay = 80,
- .mdelay = 80,
.timeout = 100
};
diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c
index 6809c283ec5..6342e612c20 100644
--- a/drivers/acpi/i2c_ec.c
+++ b/drivers/acpi/i2c_ec.c
@@ -293,7 +293,7 @@ static u32 acpi_ec_smb_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC);
}
-static struct i2c_algorithm acpi_ec_smbus_algorithm = {
+static const struct i2c_algorithm acpi_ec_smbus_algorithm = {
.smbus_xfer = acpi_ec_smb_access,
.functionality = acpi_ec_smb_func,
};
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 507f051d1ce..20beea778ea 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -1079,7 +1079,7 @@ acpi_status acpi_os_purge_cache(acpi_cache_t * cache)
acpi_status acpi_os_delete_cache(acpi_cache_t * cache)
{
- (void)kmem_cache_destroy(cache);
+ kmem_cache_destroy(cache);
return (AE_OK);
}
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 99837d932f3..3f4aa0c99ee 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -311,7 +311,7 @@ config PATA_JMICRON
config PATA_LEGACY
tristate "Legacy ISA PATA support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ depends on ISA && EXPERIMENTAL
help
This option enables support for ISA/VLB bus legacy PATA
ports and allows them to be accessed via the new ATA layer.
@@ -400,6 +400,7 @@ config PATA_PDC_OLD
config PATA_QDI
tristate "QDI VLB PATA support"
+ depends on ISA
help
Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index 1d1c30a2fcd..377425e7139 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -143,7 +143,7 @@ static struct ata_port_operations generic_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index ab2ecccf779..5719704eb0e 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -643,11 +643,9 @@ static int piix_pata_prereset(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) {
- ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n");
- ap->eh_context.i.action &= ~ATA_EH_RESET_MASK;
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -851,7 +849,7 @@ static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev)
* @ap: Port whose timings we are configuring
* @adev: Drive in question
* @udma: udma mode, 0 - 6
- * @is_ich: set if the chip is an ICH device
+ * @isich: set if the chip is an ICH device
*
* Set UDMA mode for device, in host controller PCI config space.
*
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 753b0152afd..b4abd685036 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5453,6 +5453,11 @@ int ata_device_add(const struct ata_probe_ent *ent)
int rc;
DPRINTK("ENTER\n");
+
+ if (ent->irq == 0) {
+ dev_printk(KERN_ERR, dev, "is not available: No interrupt assigned.\n");
+ return 0;
+ }
/* alloc a container for our list of ATA ports (buses) */
host = kzalloc(sizeof(struct ata_host) +
(ent->n_ports * sizeof(void *)), GFP_KERNEL);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 3fa80f09f2a..02b2b2787d9 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1515,7 +1515,11 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
if (prereset) {
rc = prereset(ap);
if (rc) {
- ata_port_printk(ap, KERN_ERR,
+ if (rc == -ENOENT) {
+ ata_port_printk(ap, KERN_DEBUG, "port disabled. ignoring.\n");
+ ap->eh_context.i.action &= ~ATA_EH_RESET_MASK;
+ } else
+ ata_port_printk(ap, KERN_ERR,
"prereset failed (errno=%d)\n", rc);
return rc;
}
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 688bb55e197..08b3a407473 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -881,7 +881,7 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
probe_ent->private_data = port[0]->private_data;
if (port_mask & ATA_PORT_PRIMARY) {
- probe_ent->irq = 14;
+ probe_ent->irq = ATA_PRIMARY_IRQ;
probe_ent->port[0].cmd_addr = ATA_PRIMARY_CMD;
probe_ent->port[0].altstatus_addr =
probe_ent->port[0].ctl_addr = ATA_PRIMARY_CTL;
@@ -896,9 +896,9 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
if (port_mask & ATA_PORT_SECONDARY) {
if (probe_ent->irq)
- probe_ent->irq2 = 15;
+ probe_ent->irq2 = ATA_SECONDARY_IRQ;
else
- probe_ent->irq = 15;
+ probe_ent->irq = ATA_SECONDARY_IRQ;
probe_ent->port[1].cmd_addr = ATA_SECONDARY_CMD;
probe_ent->port[1].altstatus_addr =
probe_ent->port[1].ctl_addr = ATA_SECONDARY_CTL;
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 8448ee6e0ee..87af3b5861a 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -369,7 +369,7 @@ static struct ata_port_operations ali_early_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -410,7 +410,7 @@ static struct ata_port_operations ali_20_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -448,7 +448,7 @@ static struct ata_port_operations ali_c2_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -485,7 +485,7 @@ static struct ata_port_operations ali_c5_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
index 3293cf9a7eb..599ee266722 100644
--- a/drivers/ata/pata_amd.c
+++ b/drivers/ata/pata_amd.c
@@ -25,7 +25,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_amd"
-#define DRV_VERSION "0.2.3"
+#define DRV_VERSION "0.2.4"
/**
* timing_setup - shared timing computation and load
@@ -137,11 +137,8 @@ static int amd_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 ata66;
- if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
+ return -ENOENT;
pci_read_config_byte(pdev, 0x42, &ata66);
if (ata66 & bitmask[ap->port_no])
@@ -167,11 +164,9 @@ static int amd_early_pre_reset(struct ata_port *ap)
{ 0x40, 1, 0x01, 0x01 }
};
- if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
+ return -ENOENT;
+
/* No host side cable detection */
ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
@@ -262,12 +257,8 @@ static int nv_pre_reset(struct ata_port *ap) {
u8 ata66;
u16 udma;
- if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
-
+ if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no]))
+ return -ENOENT;
pci_read_config_byte(pdev, 0x52, &ata66);
if (ata66 & bitmask[ap->port_no])
@@ -368,7 +359,7 @@ static struct ata_port_operations amd33_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -402,7 +393,7 @@ static struct ata_port_operations amd66_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -436,7 +427,7 @@ static struct ata_port_operations amd100_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -470,7 +461,7 @@ static struct ata_port_operations amd133_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -504,7 +495,7 @@ static struct ata_port_operations nv100_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -538,7 +529,7 @@ static struct ata_port_operations nv133_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c
index d6ef3bf1bac..c4ccb75a4f1 100644
--- a/drivers/ata/pata_artop.c
+++ b/drivers/ata/pata_artop.c
@@ -28,7 +28,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_artop"
-#define DRV_VERSION "0.4.1"
+#define DRV_VERSION "0.4.2"
/*
* The ARTOP has 33 Mhz and "over clocked" timing tables. Until we
@@ -47,11 +47,9 @@ static int artop6210_pre_reset(struct ata_port *ap)
{ 0x4AU, 1U, 0x04UL, 0x04UL }, /* port 1 */
};
- if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -90,11 +88,9 @@ static int artop6260_pre_reset(struct ata_port *ap)
u8 tmp;
/* Odd numbered device ids are the units with enable bits (the -R cards) */
- if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
+ return -ENOENT;
+
pci_read_config_byte(pdev, 0x49, &tmp);
if (tmp & (1 >> ap->port_no))
ap->cbl = ATA_CBL_PATA40;
@@ -344,7 +340,7 @@ static const struct ata_port_operations artop6210_ops = {
.bmdma_status = ata_bmdma_status,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -379,8 +375,6 @@ static const struct ata_port_operations artop6260_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index 3f78a1e54a7..6c2269b6bd3 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -22,7 +22,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_atiixp"
-#define DRV_VERSION "0.4.2"
+#define DRV_VERSION "0.4.3"
enum {
ATIIXP_IDE_PIO_TIMING = 0x40,
@@ -41,11 +41,9 @@ static int atiixp_pre_reset(struct ata_port *ap)
{ 0x48, 1, 0x08, 0x00 }
};
- if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
}
@@ -244,7 +242,7 @@ static struct ata_port_operations atiixp_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index abf1bb7bd32..e92b0ef43ec 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -301,7 +301,7 @@ static struct ata_port_operations cmd64x_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -335,7 +335,7 @@ static struct ata_port_operations cmd646r1_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -369,7 +369,7 @@ static struct ata_port_operations cmd648_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
index 792ce482851..a6c6cebd0da 100644
--- a/drivers/ata/pata_cs5520.c
+++ b/drivers/ata/pata_cs5520.c
@@ -193,8 +193,6 @@ static struct ata_port_operations cs5520_port_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c
index f3d8a3bc1e7..7bba4d954e9 100644
--- a/drivers/ata/pata_cs5530.c
+++ b/drivers/ata/pata_cs5530.c
@@ -207,7 +207,7 @@ static struct ata_port_operations cs5530_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = cs5530_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
index 69d6b425872..d64fcdceaf0 100644
--- a/drivers/ata/pata_cs5535.c
+++ b/drivers/ata/pata_cs5535.c
@@ -211,7 +211,7 @@ static struct ata_port_operations cs5535_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c
index fd55474e0d1..dfa5ac53904 100644
--- a/drivers/ata/pata_cypress.c
+++ b/drivers/ata/pata_cypress.c
@@ -162,7 +162,7 @@ static struct ata_port_operations cy82c693_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index c30bc181304..95cd1ca181f 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -22,7 +22,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_efar"
-#define DRV_VERSION "0.4.1"
+#define DRV_VERSION "0.4.2"
/**
* efar_pre_reset - check for 40/80 pin
@@ -42,11 +42,9 @@ static int efar_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 tmp;
- if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no]))
+ return -ENOENT;
+
pci_read_config_byte(pdev, 0x47, &tmp);
if (tmp & (2 >> ap->port_no))
ap->cbl = ATA_CBL_PATA40;
@@ -263,8 +261,6 @@ static const struct ata_port_operations efar_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index 94bb1dfc3f1..cf656ecbe50 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -360,7 +360,7 @@ static struct ata_port_operations hpt366_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c
index 532a7928f80..10318c0012e 100644
--- a/drivers/ata/pata_hpt37x.c
+++ b/drivers/ata/pata_hpt37x.c
@@ -793,7 +793,7 @@ static struct ata_port_operations hpt370_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -832,7 +832,7 @@ static struct ata_port_operations hpt370a_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -872,7 +872,7 @@ static struct ata_port_operations hpt372_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -912,7 +912,7 @@ static struct ata_port_operations hpt374_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c
index 06c8db079b9..5c5d4f6ab90 100644
--- a/drivers/ata/pata_hpt3x2n.c
+++ b/drivers/ata/pata_hpt3x2n.c
@@ -372,7 +372,7 @@ static struct ata_port_operations hpt3x2n_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = hpt3x2n_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
index 152770133ab..1f084ab1ccc 100644
--- a/drivers/ata/pata_hpt3x3.c
+++ b/drivers/ata/pata_hpt3x3.c
@@ -145,7 +145,7 @@ static struct ata_port_operations hpt3x3_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c
index 73948c8b727..640b8b0954f 100644
--- a/drivers/ata/pata_isapnp.c
+++ b/drivers/ata/pata_isapnp.c
@@ -52,7 +52,7 @@ static struct ata_port_operations isapnp_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index af39097d808..82a46ff4000 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -703,7 +703,7 @@ static struct ata_port_operations it821x_smart_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = it821x_smart_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -739,7 +739,7 @@ static struct ata_port_operations it821x_passthru_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = it821x_passthru_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c
index 6832a643a9e..be3a866b111 100644
--- a/drivers/ata/pata_jmicron.c
+++ b/drivers/ata/pata_jmicron.c
@@ -51,7 +51,7 @@ static int jmicron_pre_reset(struct ata_port *ap)
/* Check if our port is enabled */
pci_read_config_dword(pdev, 0x40, &control);
if ((control & port_mask) == 0)
- return 0;
+ return -ENOENT;
/* There are two basic mappings. One has the two SATA ports merged
as master/slave and the secondary as PATA, the other has only the
@@ -164,8 +164,7 @@ static const struct ata_port_operations jmicron_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- /* Timeout handling. Special recovery hooks here */
- .eng_timeout = ata_eng_timeout,
+ /* IRQ-related hooks */
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index ad37c220bb2..10231ef731d 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -161,7 +161,7 @@ static struct ata_port_operations simple_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
@@ -186,7 +186,7 @@ static struct ata_port_operations legacy_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
@@ -296,7 +296,7 @@ static struct ata_port_operations pdc20230_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = pdc_data_xfer_vlb,
.irq_handler = ata_interrupt,
@@ -348,7 +348,7 @@ static struct ata_port_operations ht6560a_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer, /* Check vlb/noirq */
.irq_handler = ata_interrupt,
@@ -411,7 +411,7 @@ static struct ata_port_operations ht6560b_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer, /* FIXME: Check 32bit and noirq */
.irq_handler = ata_interrupt,
@@ -529,7 +529,7 @@ static struct ata_port_operations opti82c611a_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -659,7 +659,7 @@ static struct ata_port_operations opti82c46x_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = opti82c46x_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c
index 1958c4ed09a..3c65393c1f0 100644
--- a/drivers/ata/pata_mpiix.c
+++ b/drivers/ata/pata_mpiix.c
@@ -18,7 +18,7 @@
* The driver conciously keeps this logic internally to avoid pushing quirky
* PATA history into the clean libata layer.
*
- * Thinkpad specific note: If you boot an MPIIX using thinkpad with a PCMCIA
+ * Thinkpad specific note: If you boot an MPIIX using a thinkpad with a PCMCIA
* hard disk present this driver will not detect it. This is not a bug. In this
* configuration the secondary port of the MPIIX is disabled and the addresses
* are decoded by the PCMCIA bridge and therefore are for a generic IDE driver
@@ -35,7 +35,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_mpiix"
-#define DRV_VERSION "0.7.1"
+#define DRV_VERSION "0.7.2"
enum {
IDETIM = 0x6C, /* IDE control register */
@@ -54,11 +54,8 @@ static int mpiix_pre_reset(struct ata_port *ap)
{ 0x6F, 1, 0x80, 0x80 }
};
- if (!pci_test_config_bits(pdev, &mpiix_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &mpiix_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
index 16cb254cb97..76eb9c90bee 100644
--- a/drivers/ata/pata_netcell.c
+++ b/drivers/ata/pata_netcell.c
@@ -90,8 +90,7 @@ static const struct ata_port_operations netcell_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- /* Timeout handling. Special recovery hooks here */
- .eng_timeout = ata_eng_timeout,
+ /* IRQ-related hooks */
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c
index 93d6646d295..2005a95f48f 100644
--- a/drivers/ata/pata_ns87410.c
+++ b/drivers/ata/pata_ns87410.c
@@ -45,11 +45,8 @@ static int ns87410_pre_reset(struct ata_port *ap)
{ 0x47, 1, 0x08, 0x08 }
};
- if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -179,7 +176,7 @@ static struct ata_port_operations ns87410_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ns87410_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index 04c618a2664..31a285ca88d 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -25,7 +25,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_oldpiix"
-#define DRV_VERSION "0.5.1"
+#define DRV_VERSION "0.5.2"
/**
* oldpiix_pre_reset - probe begin
@@ -42,11 +42,8 @@ static int oldpiix_pre_reset(struct ata_port *ap)
{ 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */
};
- if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c
index c3d01325e0e..57fe21f3a97 100644
--- a/drivers/ata/pata_opti.c
+++ b/drivers/ata/pata_opti.c
@@ -34,7 +34,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_opti"
-#define DRV_VERSION "0.2.4"
+#define DRV_VERSION "0.2.5"
enum {
READ_REG = 0, /* index of Read cycle timing register */
@@ -59,11 +59,9 @@ static int opti_pre_reset(struct ata_port *ap)
{ 0x40, 1, 0x08, 0x00 }
};
- if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -229,7 +227,7 @@ static struct ata_port_operations opti_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c
index 177a455f425..7296a20cd10 100644
--- a/drivers/ata/pata_optidma.c
+++ b/drivers/ata/pata_optidma.c
@@ -33,7 +33,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_optidma"
-#define DRV_VERSION "0.2.1"
+#define DRV_VERSION "0.2.2"
enum {
READ_REG = 0, /* index of Read cycle timing register */
@@ -59,11 +59,9 @@ static int optidma_pre_reset(struct ata_port *ap)
0x40, 1, 0x08, 0x00
};
- if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits)) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -388,7 +386,7 @@ static struct ata_port_operations optidma_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -423,7 +421,7 @@ static struct ata_port_operations optiplus_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 62b25cda409..cb501e145a4 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -87,7 +87,7 @@ static struct ata_port_operations pcmcia_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 31ab9c88620..bd4ed6734ed 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -36,7 +36,7 @@
#include <asm/io.h>
#define DRV_NAME "pata_pdc2027x"
-#define DRV_VERSION "0.74-ac3"
+#define DRV_VERSION "0.74-ac5"
#undef PDC_DEBUG
#ifdef PDC_DEBUG
@@ -311,10 +311,8 @@ static inline int pdc2027x_port_enabled(struct ata_port *ap)
static int pdc2027x_prereset(struct ata_port *ap)
{
/* Check whether port enabled */
- if (!pdc2027x_port_enabled(ap)) {
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pdc2027x_port_enabled(ap))
+ return -ENOENT;
pdc2027x_cbl_detect(ap);
return ata_std_prereset(ap);
}
diff --git a/drivers/ata/pata_qdi.c b/drivers/ata/pata_qdi.c
index 35cfdf0ac3f..7977f471d5e 100644
--- a/drivers/ata/pata_qdi.c
+++ b/drivers/ata/pata_qdi.c
@@ -184,7 +184,7 @@ static struct ata_port_operations qdi6500_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = qdi_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = qdi_data_xfer,
.irq_handler = ata_interrupt,
@@ -212,7 +212,7 @@ static struct ata_port_operations qdi6580_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = qdi_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = qdi_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 277f8411b52..c20bcf43ed6 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -255,8 +255,6 @@ static const struct ata_port_operations radisys_pata_ops = {
.qc_issue = radisys_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index 3c6d84fd431..eccc6fd4503 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -112,7 +112,7 @@ static struct ata_port_operations rz1000_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.freeze = ata_bmdma_freeze,
diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c
index 4166c1a8a9e..107e6cd3dc0 100644
--- a/drivers/ata/pata_sc1200.c
+++ b/drivers/ata/pata_sc1200.c
@@ -217,7 +217,7 @@ static struct ata_port_operations sc1200_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = sc1200_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index af456113c55..a5c8d7e121d 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -41,7 +41,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_serverworks"
-#define DRV_VERSION "0.3.6"
+#define DRV_VERSION "0.3.7"
#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
@@ -128,7 +128,7 @@ static struct sv_cable_table cable_detect[] = {
{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, dell_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, dell_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, sun_cable },
- { PCI_DEVICE_ID_SERVERWORKS_OSB4, PCI_ANY_ID, osb4_cable },
+ { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, osb4_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, csb_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, csb_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, csb_cable },
@@ -352,10 +352,12 @@ static struct ata_port_operations serverworks_osb4_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
.port_start = ata_port_start,
.port_stop = ata_port_stop,
.host_stop = ata_host_stop
@@ -385,10 +387,12 @@ static struct ata_port_operations serverworks_csb_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
.port_start = ata_port_start,
.port_stop = ata_port_stop,
.host_stop = ata_host_stop
diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
index 8f7db9638d0..c8b2e26db70 100644
--- a/drivers/ata/pata_sil680.c
+++ b/drivers/ata/pata_sil680.c
@@ -251,7 +251,7 @@ static struct ata_port_operations sil680_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index 2e555168b43..17791e2785f 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -34,7 +34,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_sis"
-#define DRV_VERSION "0.4.3"
+#define DRV_VERSION "0.4.4"
struct sis_chipset {
u16 device; /* PCI host ID */
@@ -74,11 +74,9 @@ static int sis_133_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u16 tmp;
- if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no]))
+ return -ENOENT;
+
/* The top bit of this register is the cable detect bit */
pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp);
if (tmp & 0x8000)
@@ -575,8 +573,6 @@ static const struct ata_port_operations sis_133_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -610,8 +606,6 @@ static const struct ata_port_operations sis_133_early_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -646,8 +640,6 @@ static const struct ata_port_operations sis_100_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -681,8 +673,6 @@ static const struct ata_port_operations sis_66_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -716,8 +706,6 @@ static const struct ata_port_operations sis_old_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index f8499786917..5b762acc568 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -19,7 +19,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_sl82c105"
-#define DRV_VERSION "0.2.2"
+#define DRV_VERSION "0.2.3"
enum {
/*
@@ -49,11 +49,8 @@ static int sl82c105_pre_reset(struct ata_port *ap)
};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- dev_printk(KERN_INFO, &pdev->dev, "port disabled. ignoring.\n");
- return 0;
- }
+ if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -264,7 +261,7 @@ static struct ata_port_operations sl82c105_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c
index 36f788728f3..a954ed93a40 100644
--- a/drivers/ata/pata_triflex.c
+++ b/drivers/ata/pata_triflex.c
@@ -46,13 +46,13 @@
#define DRV_VERSION "0.2.5"
/**
- * triflex_probe_init - probe begin
+ * triflex_prereset - probe begin
* @ap: ATA port
*
* Set up cable type and use generic probe init
*/
-static int triflex_probe_init(struct ata_port *ap)
+static int triflex_prereset(struct ata_port *ap)
{
static const struct pci_bits triflex_enable_bits[] = {
{ 0x80, 1, 0x01, 0x01 },
@@ -61,11 +61,8 @@ static int triflex_probe_init(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -74,7 +71,7 @@ static int triflex_probe_init(struct ata_port *ap)
static void triflex_error_handler(struct ata_port *ap)
{
- ata_bmdma_drive_eh(ap, triflex_probe_init, ata_std_softreset, NULL, ata_std_postreset);
+ ata_bmdma_drive_eh(ap, triflex_prereset, ata_std_softreset, NULL, ata_std_postreset);
}
/**
@@ -221,7 +218,7 @@ static struct ata_port_operations triflex_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index 1b2ff133b16..7b5dd2343b9 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -60,7 +60,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_via"
-#define DRV_VERSION "0.1.13"
+#define DRV_VERSION "0.1.14"
/*
* The following comes directly from Vojtech Pavlik's ide/pci/via82cxxx
@@ -155,11 +155,8 @@ static int via_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no]))
+ return -ENOENT;
}
if ((config->flags & VIA_UDMA) >= VIA_UDMA_66)
@@ -325,7 +322,7 @@ static struct ata_port_operations via_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -360,7 +357,7 @@ static struct ata_port_operations via_port_ops_noirq = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index fdce6e07ecd..c01496df4a9 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -463,6 +463,7 @@ static const struct ata_port_operations mv_iie_ops = {
.qc_prep = mv_qc_prep_iie,
.qc_issue = mv_qc_issue,
+ .data_xfer = ata_mmio_data_xfer,
.eng_timeout = mv_eng_timeout,
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 27c22feebf3..8cd730fe5dd 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -484,7 +484,7 @@ static void nv_error_handler(struct ata_port *ap)
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
- struct ata_port_info *ppi;
+ struct ata_port_info *ppi[2];
struct ata_probe_ent *probe_ent;
int pci_dev_busy = 0;
int rc;
@@ -520,8 +520,8 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
rc = -ENOMEM;
- ppi = &nv_port_info[ent->driver_data];
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ ppi[0] = ppi[1] = &nv_port_info[ent->driver_data];
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
goto err_out_regions;
diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
index 9b17375d805..18d49fff8dc 100644
--- a/drivers/ata/sata_sis.c
+++ b/drivers/ata/sata_sis.c
@@ -240,7 +240,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
struct ata_probe_ent *probe_ent = NULL;
int rc;
u32 genctl;
- struct ata_port_info *ppi;
+ struct ata_port_info *ppi[2];
int pci_dev_busy = 0;
u8 pmr;
u8 port2_start;
@@ -265,8 +265,8 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
goto err_out_regions;
- ppi = &sis_port_info;
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ ppi[0] = ppi[1] = &sis_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent) {
rc = -ENOMEM;
goto err_out_regions;
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index 8fc6e800011..dd76f37be18 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -185,7 +185,7 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
struct ata_probe_ent *probe_ent;
- struct ata_port_info *ppi;
+ struct ata_port_info *ppi[2];
int rc;
unsigned int board_idx = (unsigned int) ent->driver_data;
int pci_dev_busy = 0;
@@ -211,8 +211,8 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
goto err_out_regions;
- ppi = &uli_port_info;
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ ppi[0] = ppi[1] = &uli_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent) {
rc = -ENOMEM;
goto err_out_regions;
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 7f087aef99d..a72a2389a11 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -318,9 +318,10 @@ static void vt6421_init_addrs(struct ata_probe_ent *probe_ent,
static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev)
{
struct ata_probe_ent *probe_ent;
- struct ata_port_info *ppi = &vt6420_port_info;
-
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ struct ata_port_info *ppi[2];
+
+ ppi[0] = ppi[1] = &vt6420_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
return NULL;
diff --git a/drivers/base/base.h b/drivers/base/base.h
index c3b8dc98b8a..d26644a5953 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -16,7 +16,7 @@ extern int cpu_dev_init(void);
extern int attribute_container_init(void);
extern int bus_add_device(struct device * dev);
-extern void bus_attach_device(struct device * dev);
+extern int bus_attach_device(struct device * dev);
extern void bus_remove_device(struct device * dev);
extern struct bus_type *get_bus(struct bus_type * bus);
extern void put_bus(struct bus_type * bus);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 2e954d07175..12173d16bea 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -371,12 +371,20 @@ int bus_add_device(struct device * dev)
if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
error = device_add_attrs(bus, dev);
- if (!error) {
- sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
- sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem");
- sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus");
- }
+ if (error)
+ goto out;
+ error = sysfs_create_link(&bus->devices.kobj,
+ &dev->kobj, dev->bus_id);
+ if (error)
+ goto out;
+ error = sysfs_create_link(&dev->kobj,
+ &dev->bus->subsys.kset.kobj, "subsystem");
+ if (error)
+ goto out;
+ error = sysfs_create_link(&dev->kobj,
+ &dev->bus->subsys.kset.kobj, "bus");
}
+out:
return error;
}
@@ -384,16 +392,24 @@ int bus_add_device(struct device * dev)
* bus_attach_device - add device to bus
* @dev: device tried to attach to a driver
*
+ * - Add device to bus's list of devices.
* - Try to attach to driver.
*/
-void bus_attach_device(struct device * dev)
+int bus_attach_device(struct device * dev)
{
- struct bus_type * bus = dev->bus;
+ struct bus_type *bus = dev->bus;
+ int ret = 0;
if (bus) {
- device_attach(dev);
- klist_add_tail(&dev->knode_bus, &bus->klist_devices);
+ dev->is_registered = 1;
+ ret = device_attach(dev);
+ if (ret >= 0) {
+ klist_add_tail(&dev->knode_bus, &bus->klist_devices);
+ ret = 0;
+ } else
+ dev->is_registered = 0;
}
+ return ret;
}
/**
@@ -412,7 +428,8 @@ void bus_remove_device(struct device * dev)
sysfs_remove_link(&dev->kobj, "bus");
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
device_remove_attrs(dev->bus, dev);
- klist_remove(&dev->knode_bus);
+ dev->is_registered = 0;
+ klist_del(&dev->knode_bus);
pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id);
device_release_driver(dev);
put_bus(dev->bus);
@@ -455,10 +472,17 @@ static void driver_remove_attrs(struct bus_type * bus, struct device_driver * dr
* Thanks to drivers making their tables __devinit, we can't allow manual
* bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
*/
-static void add_bind_files(struct device_driver *drv)
+static int __must_check add_bind_files(struct device_driver *drv)
{
- driver_create_file(drv, &driver_attr_unbind);
- driver_create_file(drv, &driver_attr_bind);
+ int ret;
+
+ ret = driver_create_file(drv, &driver_attr_unbind);
+ if (ret == 0) {
+ ret = driver_create_file(drv, &driver_attr_bind);
+ if (ret)
+ driver_remove_file(drv, &driver_attr_unbind);
+ }
+ return ret;
}
static void remove_bind_files(struct device_driver *drv)
@@ -467,7 +491,7 @@ static void remove_bind_files(struct device_driver *drv)
driver_remove_file(drv, &driver_attr_unbind);
}
#else
-static inline void add_bind_files(struct device_driver *drv) {}
+static inline int add_bind_files(struct device_driver *drv) { return 0; }
static inline void remove_bind_files(struct device_driver *drv) {}
#endif
@@ -476,7 +500,7 @@ static inline void remove_bind_files(struct device_driver *drv) {}
* @drv: driver.
*
*/
-int bus_add_driver(struct device_driver * drv)
+int bus_add_driver(struct device_driver *drv)
{
struct bus_type * bus = get_bus(drv->bus);
int error = 0;
@@ -484,27 +508,39 @@ int bus_add_driver(struct device_driver * drv)
if (bus) {
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
- if (error) {
- put_bus(bus);
- return error;
- }
+ if (error)
+ goto out_put_bus;
drv->kobj.kset = &bus->drivers;
- if ((error = kobject_register(&drv->kobj))) {
- put_bus(bus);
- return error;
- }
+ if ((error = kobject_register(&drv->kobj)))
+ goto out_put_bus;
- driver_attach(drv);
+ error = driver_attach(drv);
+ if (error)
+ goto out_unregister;
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
- driver_add_attrs(bus, drv);
- add_bind_files(drv);
+ error = driver_add_attrs(bus, drv);
+ if (error) {
+ /* How the hell do we get out of this pickle? Give up */
+ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
+ __FUNCTION__, drv->name);
+ }
+ error = add_bind_files(drv);
+ if (error) {
+ /* Ditto */
+ printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
+ __FUNCTION__, drv->name);
+ }
}
return error;
+out_unregister:
+ kobject_unregister(&drv->kobj);
+out_put_bus:
+ put_bus(bus);
+ return error;
}
-
/**
* bus_remove_driver - delete driver from bus's knowledge.
* @drv: driver.
@@ -530,16 +566,21 @@ void bus_remove_driver(struct device_driver * drv)
/* Helper for bus_rescan_devices's iter */
-static int bus_rescan_devices_helper(struct device *dev, void *data)
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+ void *data)
{
+ int ret = 0;
+
if (!dev->driver) {
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
- device_attach(dev);
+ ret = device_attach(dev);
if (dev->parent)
up(&dev->parent->sem);
+ if (ret > 0)
+ ret = 0;
}
- return 0;
+ return ret < 0 ? ret : 0;
}
/**
@@ -550,9 +591,9 @@ static int bus_rescan_devices_helper(struct device *dev, void *data)
* attached and rescan it against existing drivers to see if it matches
* any by calling device_attach() for the unbound devices.
*/
-void bus_rescan_devices(struct bus_type * bus)
+int bus_rescan_devices(struct bus_type * bus)
{
- bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
+ return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
}
/**
@@ -564,7 +605,7 @@ void bus_rescan_devices(struct bus_type * bus)
* to use if probing criteria changed during a devices lifetime and
* driver attachment should change accordingly.
*/
-void device_reprobe(struct device *dev)
+int device_reprobe(struct device *dev)
{
if (dev->driver) {
if (dev->parent) /* Needed for USB */
@@ -573,14 +614,14 @@ void device_reprobe(struct device *dev)
if (dev->parent)
up(&dev->parent->sem);
}
-
- bus_rescan_devices_helper(dev, NULL);
+ return bus_rescan_devices_helper(dev, NULL);
}
EXPORT_SYMBOL_GPL(device_reprobe);
-struct bus_type * get_bus(struct bus_type * bus)
+struct bus_type *get_bus(struct bus_type *bus)
{
- return bus ? container_of(subsys_get(&bus->subsys), struct bus_type, subsys) : NULL;
+ return bus ? container_of(subsys_get(&bus->subsys),
+ struct bus_type, subsys) : NULL;
}
void put_bus(struct bus_type * bus)
@@ -655,22 +696,6 @@ static void klist_devices_put(struct klist_node *n)
put_device(dev);
}
-static void klist_drivers_get(struct klist_node *n)
-{
- struct device_driver *drv = container_of(n, struct device_driver,
- knode_bus);
-
- get_driver(drv);
-}
-
-static void klist_drivers_put(struct klist_node *n)
-{
- struct device_driver *drv = container_of(n, struct device_driver,
- knode_bus);
-
- put_driver(drv);
-}
-
/**
* bus_register - register a bus with the system.
* @bus: bus.
@@ -706,7 +731,7 @@ int bus_register(struct bus_type * bus)
goto bus_drivers_fail;
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
- klist_init(&bus->klist_drivers, klist_drivers_get, klist_drivers_put);
+ klist_init(&bus->klist_drivers, NULL, NULL);
bus_add_attrs(bus);
pr_debug("bus type '%s' registered\n", bus->name);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index de8908320f2..b32b77ff2dc 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -19,6 +19,8 @@
#include <linux/slab.h>
#include "base.h"
+extern struct subsystem devices_subsys;
+
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
@@ -197,7 +199,7 @@ static int class_device_create_uevent(struct class_device *class_dev,
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
-struct class *class_create(struct module *owner, char *name)
+struct class *class_create(struct module *owner, const char *name)
{
struct class *cls;
int retval;
@@ -226,7 +228,7 @@ error:
/**
* class_destroy - destroys a struct class structure
- * @cs: pointer to the struct class that is to be destroyed
+ * @cls: pointer to the struct class that is to be destroyed
*
* Note, the pointer to be destroyed must have been created with a call
* to class_create().
@@ -361,7 +363,7 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
if (class_dev->dev) {
- /* add physical device, backing this device */
+ /* add device, backing this class device (deprecated) */
struct device *dev = class_dev->dev;
char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
@@ -656,9 +658,9 @@ int class_device_register(struct class_device *class_dev)
/**
* class_device_create - creates a class device and registers it with sysfs
- * @cs: pointer to the struct class that this device should be registered to.
+ * @cls: pointer to the struct class that this device should be registered to.
* @parent: pointer to the parent struct class_device of this new device, if any.
- * @dev: the dev_t for the char device to be added.
+ * @devt: the dev_t for the char device to be added.
* @device: a pointer to a struct device that is assiociated with this class device.
* @fmt: string for the class device's name
*
@@ -679,7 +681,8 @@ int class_device_register(struct class_device *class_dev)
struct class_device *class_device_create(struct class *cls,
struct class_device *parent,
dev_t devt,
- struct device *device, char *fmt, ...)
+ struct device *device,
+ const char *fmt, ...)
{
va_list args;
struct class_device *class_dev = NULL;
@@ -763,7 +766,7 @@ void class_device_unregister(struct class_device *class_dev)
/**
* class_device_destroy - removes a class device that was created with class_device_create()
* @cls: the pointer to the struct class that this device was registered * with.
- * @dev: the dev_t of the device that was previously registered.
+ * @devt: the dev_t of the device that was previously registered.
*
* This call unregisters and cleans up a class device that was created with a
* call to class_device_create()
@@ -839,6 +842,7 @@ int class_interface_register(struct class_interface *class_intf)
{
struct class *parent;
struct class_device *class_dev;
+ struct device *dev;
if (!class_intf || !class_intf->class)
return -ENODEV;
@@ -853,6 +857,10 @@ int class_interface_register(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node)
class_intf->add(class_dev, class_intf);
}
+ if (class_intf->add_dev) {
+ list_for_each_entry(dev, &parent->devices, node)
+ class_intf->add_dev(dev, class_intf);
+ }
up(&parent->sem);
return 0;
@@ -862,6 +870,7 @@ void class_interface_unregister(struct class_interface *class_intf)
{
struct class * parent = class_intf->class;
struct class_device *class_dev;
+ struct device *dev;
if (!parent)
return;
@@ -872,12 +881,31 @@ void class_interface_unregister(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node)
class_intf->remove(class_dev, class_intf);
}
+ if (class_intf->remove_dev) {
+ list_for_each_entry(dev, &parent->devices, node)
+ class_intf->remove_dev(dev, class_intf);
+ }
up(&parent->sem);
class_put(parent);
}
+int virtual_device_parent(struct device *dev)
+{
+ if (!dev->class)
+ return -ENODEV;
+
+ if (!dev->class->virtual_dir) {
+ static struct kobject *virtual_dir = NULL;
+
+ if (!virtual_dir)
+ virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
+ dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
+ }
+ dev->kobj.parent = dev->class->virtual_dir;
+ return 0;
+}
int __init classes_init(void)
{
diff --git a/drivers/base/core.c b/drivers/base/core.c
index be6b5bc0677..b224bb43ff6 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3,6 +3,8 @@
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2006 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2006 Novell, Inc.
*
* This file is released under the GPLv2
*
@@ -92,6 +94,8 @@ static void device_release(struct kobject * kobj)
if (dev->release)
dev->release(dev);
+ else if (dev->class && dev->class->dev_release)
+ dev->class->dev_release(dev);
else {
printk(KERN_ERR "Device '%s' does not have a release() function, "
"it is broken and must be fixed.\n",
@@ -149,17 +153,21 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
"MINOR=%u", MINOR(dev->devt));
}
- /* add bus name of physical device */
+ /* add bus name (same as SUBSYSTEM, deprecated) */
if (dev->bus)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVBUS=%s", dev->bus->name);
- /* add driver name of physical device */
- if (dev->driver)
+ /* add driver name (PHYSDEV* values are deprecated)*/
+ if (dev->driver) {
+ add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "DRIVER=%s", dev->driver->name);
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s", dev->driver->name);
+ }
/* terminate, set to next free slot, shrink available space */
envp[i] = NULL;
@@ -177,6 +185,15 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
}
}
+ if (dev->class && dev->class->dev_uevent) {
+ /* have the class specific function add its stuff */
+ retval = dev->class->dev_uevent(dev, envp, num_envp, buffer, buffer_size);
+ if (retval) {
+ pr_debug("%s - dev_uevent() returned %d\n",
+ __FUNCTION__, retval);
+ }
+ }
+
return retval;
}
@@ -193,6 +210,72 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
return count;
}
+static int device_add_groups(struct device *dev)
+{
+ int i;
+ int error = 0;
+
+ if (dev->groups) {
+ for (i = 0; dev->groups[i]; i++) {
+ error = sysfs_create_group(&dev->kobj, dev->groups[i]);
+ if (error) {
+ while (--i >= 0)
+ sysfs_remove_group(&dev->kobj, dev->groups[i]);
+ goto out;
+ }
+ }
+ }
+out:
+ return error;
+}
+
+static void device_remove_groups(struct device *dev)
+{
+ int i;
+ if (dev->groups) {
+ for (i = 0; dev->groups[i]; i++) {
+ sysfs_remove_group(&dev->kobj, dev->groups[i]);
+ }
+ }
+}
+
+static int device_add_attrs(struct device *dev)
+{
+ struct class *class = dev->class;
+ int error = 0;
+ int i;
+
+ if (!class)
+ return 0;
+
+ if (class->dev_attrs) {
+ for (i = 0; attr_name(class->dev_attrs[i]); i++) {
+ error = device_create_file(dev, &class->dev_attrs[i]);
+ if (error)
+ break;
+ }
+ }
+ if (error)
+ while (--i >= 0)
+ device_remove_file(dev, &class->dev_attrs[i]);
+ return error;
+}
+
+static void device_remove_attrs(struct device *dev)
+{
+ struct class *class = dev->class;
+ int i;
+
+ if (!class)
+ return;
+
+ if (class->dev_attrs) {
+ for (i = 0; attr_name(class->dev_attrs[i]); i++)
+ device_remove_file(dev, &class->dev_attrs[i]);
+ }
+}
+
+
static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -236,6 +319,32 @@ void device_remove_file(struct device * dev, struct device_attribute * attr)
}
}
+/**
+ * device_create_bin_file - create sysfs binary attribute file for device.
+ * @dev: device.
+ * @attr: device binary attribute descriptor.
+ */
+int device_create_bin_file(struct device *dev, struct bin_attribute *attr)
+{
+ int error = -EINVAL;
+ if (dev)
+ error = sysfs_create_bin_file(&dev->kobj, attr);
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_create_bin_file);
+
+/**
+ * device_remove_bin_file - remove sysfs binary attribute file
+ * @dev: device.
+ * @attr: device binary attribute descriptor.
+ */
+void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
+{
+ if (dev)
+ sysfs_remove_bin_file(&dev->kobj, attr);
+}
+EXPORT_SYMBOL_GPL(device_remove_bin_file);
+
static void klist_children_get(struct klist_node *n)
{
struct device *dev = container_of(n, struct device, knode_parent);
@@ -289,12 +398,20 @@ int device_add(struct device *dev)
{
struct device *parent = NULL;
char *class_name = NULL;
+ struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev || !strlen(dev->bus_id))
goto Error;
+ /* if this is a class device, and has no parent, create one */
+ if ((dev->class) && (dev->parent == NULL)) {
+ error = virtual_device_parent(dev);
+ if (error)
+ goto Error;
+ }
+
parent = get_device(dev->parent);
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
@@ -307,6 +424,10 @@ int device_add(struct device *dev)
if ((error = kobject_add(&dev->kobj)))
goto Error;
+ /* notify platform of device entry */
+ if (platform_notify)
+ platform_notify(dev);
+
dev->uevent_attr.attr.name = "uevent";
dev->uevent_attr.attr.mode = S_IWUSR;
if (dev->driver)
@@ -340,12 +461,17 @@ int device_add(struct device *dev)
"subsystem");
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
dev->bus_id);
-
- sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
- class_name = make_class_name(dev->class->name, &dev->kobj);
- sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
+ if (parent) {
+ sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
+ }
}
+ if ((error = device_add_attrs(dev)))
+ goto AttrsError;
+ if ((error = device_add_groups(dev)))
+ goto GroupError;
if ((error = device_pm_add(dev)))
goto PMError;
if ((error = bus_add_device(dev)))
@@ -356,15 +482,16 @@ int device_add(struct device *dev)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
- /* tie the class to the device */
down(&dev->class->sem);
+ /* tie the class to the device */
list_add_tail(&dev->node, &dev->class->devices);
+
+ /* notify any interfaces that the device is here */
+ list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ if (class_intf->add_dev)
+ class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
-
- /* notify platform of device entry */
- if (platform_notify)
- platform_notify(dev);
Done:
kfree(class_name);
put_device(dev);
@@ -372,6 +499,10 @@ int device_add(struct device *dev)
BusError:
device_pm_remove(dev);
PMError:
+ device_remove_groups(dev);
+ GroupError:
+ device_remove_attrs(dev);
+ AttrsError:
if (dev->devt_attr) {
device_remove_file(dev, dev->devt_attr);
kfree(dev->devt_attr);
@@ -449,6 +580,7 @@ void device_del(struct device * dev)
{
struct device * parent = dev->parent;
char *class_name = NULL;
+ struct class_interface *class_intf;
if (parent)
klist_del(&dev->knode_parent);
@@ -458,14 +590,23 @@ void device_del(struct device * dev)
sysfs_remove_link(&dev->kobj, "subsystem");
sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
class_name = make_class_name(dev->class->name, &dev->kobj);
- sysfs_remove_link(&dev->kobj, "device");
- sysfs_remove_link(&dev->parent->kobj, class_name);
+ if (parent) {
+ sysfs_remove_link(&dev->kobj, "device");
+ sysfs_remove_link(&dev->parent->kobj, class_name);
+ }
kfree(class_name);
down(&dev->class->sem);
+ /* notify any interfaces that the device is now gone */
+ list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ if (class_intf->remove_dev)
+ class_intf->remove_dev(dev, class_intf);
+ /* remove the device from the class list */
list_del_init(&dev->node);
up(&dev->class->sem);
}
device_remove_file(dev, &dev->uevent_attr);
+ device_remove_groups(dev);
+ device_remove_attrs(dev);
/* Notify the platform of the removal, in case they
* need to do anything...
@@ -579,7 +720,7 @@ static void device_create_release(struct device *dev)
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
- dev_t devt, char *fmt, ...)
+ dev_t devt, const char *fmt, ...)
{
va_list args;
struct device *dev = NULL;
@@ -587,10 +728,6 @@ struct device *device_create(struct class *class, struct device *parent,
if (class == NULL || IS_ERR(class))
goto error;
- if (parent == NULL) {
- printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__);
- goto error;
- }
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
@@ -644,3 +781,58 @@ void device_destroy(struct class *class, dev_t devt)
device_unregister(dev);
}
EXPORT_SYMBOL_GPL(device_destroy);
+
+/**
+ * device_rename - renames a device
+ * @dev: the pointer to the struct device to be renamed
+ * @new_name: the new name of the device
+ */
+int device_rename(struct device *dev, char *new_name)
+{
+ char *old_class_name = NULL;
+ char *new_class_name = NULL;
+ char *old_symlink_name = NULL;
+ int error;
+
+ dev = get_device(dev);
+ if (!dev)
+ return -EINVAL;
+
+ pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name);
+
+ if ((dev->class) && (dev->parent))
+ old_class_name = make_class_name(dev->class->name, &dev->kobj);
+
+ if (dev->class) {
+ old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
+ if (!old_symlink_name)
+ return -ENOMEM;
+ strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE);
+ }
+
+ strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
+
+ error = kobject_rename(&dev->kobj, new_name);
+
+ if (old_class_name) {
+ new_class_name = make_class_name(dev->class->name, &dev->kobj);
+ if (new_class_name) {
+ sysfs_create_link(&dev->parent->kobj, &dev->kobj,
+ new_class_name);
+ sysfs_remove_link(&dev->parent->kobj, old_class_name);
+ }
+ }
+ if (dev->class) {
+ sysfs_remove_link(&dev->class->subsys.kset.kobj,
+ old_symlink_name);
+ sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
+ dev->bus_id);
+ }
+ put_device(dev);
+
+ kfree(old_class_name);
+ kfree(new_class_name);
+ kfree(old_symlink_name);
+
+ return error;
+}
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 889c7111123..b5f43c3e44f 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/kthread.h>
#include "base.h"
#include "power/power.h"
@@ -38,66 +39,73 @@
*
* This function must be called with @dev->sem held.
*/
-void device_bind_driver(struct device * dev)
+int device_bind_driver(struct device *dev)
{
- if (klist_node_attached(&dev->knode_driver))
- return;
+ int ret;
+
+ if (klist_node_attached(&dev->knode_driver)) {
+ printk(KERN_WARNING "%s: device %s already bound\n",
+ __FUNCTION__, kobject_name(&dev->kobj));
+ return 0;
+ }
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id, dev->driver->name);
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
- sysfs_create_link(&dev->driver->kobj, &dev->kobj,
+ ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
kobject_name(&dev->kobj));
- sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
+ if (ret == 0) {
+ ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj,
+ "driver");
+ if (ret)
+ sysfs_remove_link(&dev->driver->kobj,
+ kobject_name(&dev->kobj));
+ }
+ return ret;
}
-/**
- * driver_probe_device - attempt to bind device & driver.
- * @drv: driver.
- * @dev: device.
- *
- * First, we call the bus's match function, if one present, which
- * should compare the device IDs the driver supports with the
- * device IDs of the device. Note we don't do this ourselves
- * because we don't know the format of the ID structures, nor what
- * is to be considered a match and what is not.
- *
- * This function returns 1 if a match is found, an error if one
- * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise.
- *
- * This function must be called with @dev->sem held. When called
- * for a USB interface, @dev->parent->sem must be held as well.
- */
-int driver_probe_device(struct device_driver * drv, struct device * dev)
+struct stupid_thread_structure {
+ struct device_driver *drv;
+ struct device *dev;
+};
+
+static atomic_t probe_count = ATOMIC_INIT(0);
+static int really_probe(void *void_data)
{
+ struct stupid_thread_structure *data = void_data;
+ struct device_driver *drv = data->drv;
+ struct device *dev = data->dev;
int ret = 0;
- if (drv->bus->match && !drv->bus->match(dev, drv))
- goto Done;
+ atomic_inc(&probe_count);
+ pr_debug("%s: Probing driver %s with device %s\n",
+ drv->bus->name, drv->name, dev->bus_id);
- pr_debug("%s: Matched Device %s with Driver %s\n",
- drv->bus->name, dev->bus_id, drv->name);
dev->driver = drv;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) {
dev->driver = NULL;
- goto ProbeFailed;
+ goto probe_failed;
}
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret) {
dev->driver = NULL;
- goto ProbeFailed;
+ goto probe_failed;
}
}
- device_bind_driver(dev);
+ if (device_bind_driver(dev)) {
+ printk(KERN_ERR "%s: device_bind_driver(%s) failed\n",
+ __FUNCTION__, dev->bus_id);
+ /* How does undo a ->probe? We're screwed. */
+ }
ret = 1;
pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
- goto Done;
+ goto done;
- ProbeFailed:
+probe_failed:
if (ret == -ENODEV || ret == -ENXIO) {
/* Driver matched, but didn't support device
* or device not found.
@@ -110,7 +118,71 @@ int driver_probe_device(struct device_driver * drv, struct device * dev)
"%s: probe of %s failed with error %d\n",
drv->name, dev->bus_id, ret);
}
- Done:
+done:
+ kfree(data);
+ atomic_dec(&probe_count);
+ return ret;
+}
+
+/**
+ * driver_probe_done
+ * Determine if the probe sequence is finished or not.
+ *
+ * Should somehow figure out how to use a semaphore, not an atomic variable...
+ */
+int driver_probe_done(void)
+{
+ pr_debug("%s: probe_count = %d\n", __FUNCTION__,
+ atomic_read(&probe_count));
+ if (atomic_read(&probe_count))
+ return -EBUSY;
+ return 0;
+}
+
+/**
+ * driver_probe_device - attempt to bind device & driver together
+ * @drv: driver to bind a device to
+ * @dev: device to try to bind to the driver
+ *
+ * First, we call the bus's match function, if one present, which should
+ * compare the device IDs the driver supports with the device IDs of the
+ * device. Note we don't do this ourselves because we don't know the
+ * format of the ID structures, nor what is to be considered a match and
+ * what is not.
+ *
+ * This function returns 1 if a match is found, an error if one occurs
+ * (that is not -ENODEV or -ENXIO), and 0 otherwise.
+ *
+ * This function must be called with @dev->sem held. When called for a
+ * USB interface, @dev->parent->sem must be held as well.
+ */
+int driver_probe_device(struct device_driver * drv, struct device * dev)
+{
+ struct stupid_thread_structure *data;
+ struct task_struct *probe_task;
+ int ret = 0;
+
+ if (!device_is_registered(dev))
+ return -ENODEV;
+ if (drv->bus->match && !drv->bus->match(dev, drv))
+ goto done;
+
+ pr_debug("%s: Matched Device %s with Driver %s\n",
+ drv->bus->name, dev->bus_id, drv->name);
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ data->drv = drv;
+ data->dev = dev;
+
+ if (drv->multithread_probe) {
+ probe_task = kthread_run(really_probe, data,
+ "probe-%s", dev->bus_id);
+ if (IS_ERR(probe_task))
+ ret = PTR_ERR(probe_task);
+ } else
+ ret = really_probe(data);
+
+done:
return ret;
}
@@ -139,8 +211,9 @@ int device_attach(struct device * dev)
down(&dev->sem);
if (dev->driver) {
- device_bind_driver(dev);
- ret = 1;
+ ret = device_bind_driver(dev);
+ if (ret == 0)
+ ret = 1;
} else
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
up(&dev->sem);
@@ -182,9 +255,9 @@ static int __driver_attach(struct device * dev, void * data)
* returns 0 and the @dev->driver is set, we've found a
* compatible pair.
*/
-void driver_attach(struct device_driver * drv)
+int driver_attach(struct device_driver * drv)
{
- bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
+ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
/**
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 562600dd540..1214cbd17d8 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -142,20 +142,6 @@ void put_driver(struct device_driver * drv)
kobject_put(&drv->kobj);
}
-static void klist_devices_get(struct klist_node *n)
-{
- struct device *dev = container_of(n, struct device, knode_driver);
-
- get_device(dev);
-}
-
-static void klist_devices_put(struct klist_node *n)
-{
- struct device *dev = container_of(n, struct device, knode_driver);
-
- put_device(dev);
-}
-
/**
* driver_register - register driver with bus
* @drv: driver to register
@@ -175,7 +161,7 @@ int driver_register(struct device_driver * drv)
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
- klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
+ klist_init(&drv->klist_devices, NULL, NULL);
init_completion(&drv->unloaded);
return bus_add_driver(drv);
}
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 5d6c011183f..14615694ae9 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
+#include <linux/kthread.h>
#include <linux/firmware.h>
#include "base.h"
@@ -511,7 +512,6 @@ request_firmware_work_func(void *arg)
WARN_ON(1);
return 0;
}
- daemonize("%s/%s", "firmware", fw_work->name);
ret = _request_firmware(&fw, fw_work->name, fw_work->device,
fw_work->uevent);
if (ret < 0)
@@ -546,9 +546,9 @@ request_firmware_nowait(
const char *name, struct device *device, void *context,
void (*cont)(const struct firmware *fw, void *context))
{
+ struct task_struct *task;
struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
GFP_ATOMIC);
- int ret;
if (!fw_work)
return -ENOMEM;
@@ -566,14 +566,14 @@ request_firmware_nowait(
.uevent = uevent,
};
- ret = kernel_thread(request_firmware_work_func, fw_work,
- CLONE_FS | CLONE_FILES);
+ task = kthread_run(request_firmware_work_func, fw_work,
+ "firmware/%s", name);
- if (ret < 0) {
+ if (IS_ERR(task)) {
fw_work->cont(NULL, fw_work->context);
module_put(fw_work->module);
kfree(fw_work);
- return ret;
+ return PTR_ERR(task);
}
return 0;
}
@@ -602,7 +602,7 @@ firmware_class_exit(void)
class_unregister(&firmware_class);
}
-module_init(firmware_class_init);
+fs_initcall(firmware_class_init);
module_exit(firmware_class_exit);
EXPORT_SYMBOL(release_firmware);
diff --git a/drivers/base/node.c b/drivers/base/node.c
index e9b0957f15d..001e6f6b9c1 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -54,10 +54,12 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
"Node %d MemUsed: %8lu kB\n"
"Node %d Active: %8lu kB\n"
"Node %d Inactive: %8lu kB\n"
+#ifdef CONFIG_HIGHMEM
"Node %d HighTotal: %8lu kB\n"
"Node %d HighFree: %8lu kB\n"
"Node %d LowTotal: %8lu kB\n"
"Node %d LowFree: %8lu kB\n"
+#endif
"Node %d Dirty: %8lu kB\n"
"Node %d Writeback: %8lu kB\n"
"Node %d FilePages: %8lu kB\n"
@@ -66,16 +68,20 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
"Node %d PageTables: %8lu kB\n"
"Node %d NFS_Unstable: %8lu kB\n"
"Node %d Bounce: %8lu kB\n"
- "Node %d Slab: %8lu kB\n",
+ "Node %d Slab: %8lu kB\n"
+ "Node %d SReclaimable: %8lu kB\n"
+ "Node %d SUnreclaim: %8lu kB\n",
nid, K(i.totalram),
nid, K(i.freeram),
nid, K(i.totalram - i.freeram),
nid, K(active),
nid, K(inactive),
+#ifdef CONFIG_HIGHMEM
nid, K(i.totalhigh),
nid, K(i.freehigh),
nid, K(i.totalram - i.totalhigh),
nid, K(i.freeram - i.freehigh),
+#endif
nid, K(node_page_state(nid, NR_FILE_DIRTY)),
nid, K(node_page_state(nid, NR_WRITEBACK)),
nid, K(node_page_state(nid, NR_FILE_PAGES)),
@@ -84,7 +90,10 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
nid, K(node_page_state(nid, NR_PAGETABLE)),
nid, K(node_page_state(nid, NR_UNSTABLE_NFS)),
nid, K(node_page_state(nid, NR_BOUNCE)),
- nid, K(node_page_state(nid, NR_SLAB)));
+ nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE) +
+ node_page_state(nid, NR_SLAB_UNRECLAIMABLE)),
+ nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE)),
+ nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE)));
n += hugetlb_report_node_meminfo(nid, buf + n);
return n;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 2b8755db76c..940ce41f188 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -505,12 +505,36 @@ static int platform_match(struct device * dev, struct device_driver * drv)
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
-static int platform_suspend(struct device * dev, pm_message_t state)
+static int platform_suspend(struct device *dev, pm_message_t mesg)
{
int ret = 0;
if (dev->driver && dev->driver->suspend)
- ret = dev->driver->suspend(dev, state);
+ ret = dev->driver->suspend(dev, mesg);
+
+ return ret;
+}
+
+static int platform_suspend_late(struct device *dev, pm_message_t mesg)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ int ret = 0;
+
+ if (dev->driver && drv->suspend_late)
+ ret = drv->suspend_late(pdev, mesg);
+
+ return ret;
+}
+
+static int platform_resume_early(struct device *dev)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ int ret = 0;
+
+ if (dev->driver && drv->resume_early)
+ ret = drv->resume_early(pdev);
return ret;
}
@@ -531,6 +555,8 @@ struct bus_type platform_bus_type = {
.match = platform_match,
.uevent = platform_uevent,
.suspend = platform_suspend,
+ .suspend_late = platform_suspend_late,
+ .resume_early = platform_resume_early,
.resume = platform_resume,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 826093ef4c7..020be36705a 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -38,13 +38,35 @@ int resume_device(struct device * dev)
dev_dbg(dev,"resuming\n");
error = dev->bus->resume(dev);
}
+ if (dev->class && dev->class->resume) {
+ dev_dbg(dev,"class resume\n");
+ error = dev->class->resume(dev);
+ }
up(&dev->sem);
TRACE_RESUME(error);
return error;
}
+static int resume_device_early(struct device * dev)
+{
+ int error = 0;
+ TRACE_DEVICE(dev);
+ TRACE_RESUME(0);
+ if (dev->bus && dev->bus->resume_early) {
+ dev_dbg(dev,"EARLY resume\n");
+ error = dev->bus->resume_early(dev);
+ }
+ TRACE_RESUME(error);
+ return error;
+}
+
+/*
+ * Resume the devices that have either not gone through
+ * the late suspend, or that did go through it but also
+ * went through the early resume
+ */
void dpm_resume(void)
{
down(&dpm_list_sem);
@@ -74,6 +96,7 @@ void dpm_resume(void)
void device_resume(void)
{
+ might_sleep();
down(&dpm_sem);
dpm_resume();
up(&dpm_sem);
@@ -83,12 +106,12 @@ EXPORT_SYMBOL_GPL(device_resume);
/**
- * device_power_up_irq - Power on some devices.
+ * dpm_power_up - Power on some devices.
*
* Walk the dpm_off_irq list and power each device up. This
* is used for devices that required they be powered down with
- * interrupts disabled. As devices are powered on, they are moved to
- * the dpm_suspended list.
+ * interrupts disabled. As devices are powered on, they are moved
+ * to the dpm_active list.
*
* Interrupts must be disabled when calling this.
*/
@@ -99,16 +122,14 @@ void dpm_power_up(void)
struct list_head * entry = dpm_off_irq.next;
struct device * dev = to_device(entry);
- get_device(dev);
- list_move_tail(entry, &dpm_active);
- resume_device(dev);
- put_device(dev);
+ list_move_tail(entry, &dpm_off);
+ resume_device_early(dev);
}
}
/**
- * device_pm_power_up - Turn on all devices that need special attention.
+ * device_power_up - Turn on all devices that need special attention.
*
* Power on system devices then devices that required we shut them down
* with interrupts disabled.
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 69509e02f70..ece136bf97e 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event)
switch (event) {
case PM_EVENT_SUSPEND: return "suspend";
case PM_EVENT_FREEZE: return "freeze";
+ case PM_EVENT_PRETHAW: return "prethaw";
default: return "(unknown suspend event)";
}
}
@@ -65,7 +66,19 @@ int suspend_device(struct device * dev, pm_message_t state)
dev->power.prev_state = dev->power.power_state;
- if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
+ if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
+ dev_dbg(dev, "class %s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->class->suspend(dev, state);
+ suspend_report_result(dev->class->suspend, error);
+ }
+
+ if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "%s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
@@ -81,15 +94,42 @@ int suspend_device(struct device * dev, pm_message_t state)
}
+/*
+ * This is called with interrupts off, only a single CPU
+ * running. We can't do down() on a semaphore (and we don't
+ * need the protection)
+ */
+static int suspend_device_late(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
+ dev_dbg(dev, "LATE %s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->bus->suspend_late(dev, state);
+ suspend_report_result(dev->bus->suspend_late, error);
+ }
+ return error;
+}
+
/**
* device_suspend - Save state and stop all devices in system.
* @state: Power state to put each device in.
*
* Walk the dpm_active list, call ->suspend() for each device, and move
- * it to dpm_off.
- * Check the return value for each. If it returns 0, then we move the
- * the device to the dpm_off list. If it returns -EAGAIN, we move it to
- * the dpm_off_irq list. If we get a different error, try and back out.
+ * it to the dpm_off list.
+ *
+ * (For historical reasons, if it returns -EAGAIN, that used to mean
+ * that the device would be called again with interrupts disabled.
+ * These days, we use the "suspend_late()" callback for that, so we
+ * print a warning and consider it an error).
+ *
+ * If we get a different error, try and back out.
*
* If we hit a failure with any of the devices, call device_resume()
* above to bring the suspended devices back to life.
@@ -100,6 +140,7 @@ int device_suspend(pm_message_t state)
{
int error = 0;
+ might_sleep();
down(&dpm_sem);
down(&dpm_list_sem);
while (!list_empty(&dpm_active) && error == 0) {
@@ -115,39 +156,27 @@ int device_suspend(pm_message_t state)
/* Check if the device got removed */
if (!list_empty(&dev->power.entry)) {
- /* Move it to the dpm_off or dpm_off_irq list */
+ /* Move it to the dpm_off list */
if (!error)
list_move(&dev->power.entry, &dpm_off);
- else if (error == -EAGAIN) {
- list_move(&dev->power.entry, &dpm_off_irq);
- error = 0;
- }
}
if (error)
printk(KERN_ERR "Could not suspend device %s: "
- "error %d\n", kobject_name(&dev->kobj), error);
+ "error %d%s\n",
+ kobject_name(&dev->kobj), error,
+ error == -EAGAIN ? " (please convert to suspend_late)" : "");
put_device(dev);
}
up(&dpm_list_sem);
- if (error) {
- /* we failed... before resuming, bring back devices from
- * dpm_off_irq list back to main dpm_off list, we do want
- * to call resume() on them, in case they partially suspended
- * despite returning -EAGAIN
- */
- while (!list_empty(&dpm_off_irq)) {
- struct list_head * entry = dpm_off_irq.next;
- list_move(entry, &dpm_off);
- }
+ if (error)
dpm_resume();
- }
+
up(&dpm_sem);
return error;
}
EXPORT_SYMBOL_GPL(device_suspend);
-
/**
* device_power_down - Shut down special devices.
* @state: Power state to enter.
@@ -162,14 +191,17 @@ int device_power_down(pm_message_t state)
int error = 0;
struct device * dev;
- list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
- if ((error = suspend_device(dev, state)))
- break;
+ while (!list_empty(&dpm_off)) {
+ struct list_head * entry = dpm_off.prev;
+
+ dev = to_device(entry);
+ error = suspend_device_late(dev, state);
+ if (error)
+ goto Error;
+ list_move(&dev->power.entry, &dpm_off_irq);
}
- if (error)
- goto Error;
- if ((error = sysdev_suspend(state)))
- goto Error;
+
+ error = sysdev_suspend(state);
Done:
return error;
Error:
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 40d7242a07c..2d47517dbe3 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -7,22 +7,29 @@
#include "power.h"
+#ifdef CONFIG_PM_SYSFS_DEPRECATED
+
/**
* state - Control current power state of device
*
* show() returns the current power state of the device. '0' indicates
- * the device is on. Other values (1-3) indicate the device is in a low
+ * the device is on. Other values (2) indicate the device is in some low
* power state.
*
- * store() sets the current power state, which is an integer value
- * between 0-3. If the device is on ('0'), and the value written is
- * greater than 0, then the device is placed directly into the low-power
- * state (via its driver's ->suspend() method).
- * If the device is currently in a low-power state, and the value is 0,
- * the device is powered back on (via the ->resume() method).
- * If the device is in a low-power state, and a different low-power state
- * is requested, the device is first resumed, then suspended into the new
- * low-power state.
+ * store() sets the current power state, which is an integer valued
+ * 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early()
+ * methods fail this operation; those methods couldn't be called.
+ * Otherwise,
+ *
+ * - If the recorded dev->power.power_state.event matches the
+ * target value, nothing is done.
+ * - If the recorded event code is nonzero, the device is reactivated
+ * by calling bus.resume() and/or class.resume().
+ * - If the target value is nonzero, the device is suspended by
+ * calling class.suspend() and/or bus.suspend() with event code
+ * PM_EVENT_SUSPEND.
+ *
+ * This mechanism is DEPRECATED and should only be used for testing.
*/
static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
@@ -38,6 +45,10 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
pm_message_t state;
int error = -EINVAL;
+ /* disallow incomplete suspend sequences */
+ if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early))
+ return error;
+
state.event = PM_EVENT_SUSPEND;
/* Older apps expected to write "3" here - confused with PCI D3 */
if ((n == 1) && !strcmp(buf, "3"))
@@ -57,6 +68,8 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
static DEVICE_ATTR(state, 0644, state_show, state_store);
+#endif /* CONFIG_PM_SYSFS_DEPRECATED */
+
/*
* wakeup - Report/change current wakeup option for device
*
@@ -130,7 +143,9 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
static struct attribute * power_attrs[] = {
+#ifdef CONFIG_PM_SYSFS_DEPRECATED
&dev_attr_state.attr,
+#endif
&dev_attr_wakeup.attr,
NULL,
};
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 7b3b94ddddc..68b0471ad5a 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -72,6 +72,7 @@
#include <linux/completion.h>
#include <linux/highmem.h>
#include <linux/gfp.h>
+#include <linux/kthread.h>
#include <asm/uaccess.h>
@@ -522,15 +523,12 @@ static int loop_make_request(request_queue_t *q, struct bio *old_bio)
goto out;
if (unlikely(rw == WRITE && (lo->lo_flags & LO_FLAGS_READ_ONLY)))
goto out;
- lo->lo_pending++;
loop_add_bio(lo, old_bio);
+ wake_up(&lo->lo_event);
spin_unlock_irq(&lo->lo_lock);
- complete(&lo->lo_bh_done);
return 0;
out:
- if (lo->lo_pending == 0)
- complete(&lo->lo_bh_done);
spin_unlock_irq(&lo->lo_lock);
bio_io_error(old_bio, old_bio->bi_size);
return 0;
@@ -570,14 +568,18 @@ static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio)
* to avoid blocking in our make_request_fn. it also does loop decrypting
* on reads for block backed loop, as that is too heavy to do from
* b_end_io context where irqs may be disabled.
+ *
+ * Loop explanation: loop_clr_fd() sets lo_state to Lo_rundown before
+ * calling kthread_stop(). Therefore once kthread_should_stop() is
+ * true, make_request will not place any more requests. Therefore
+ * once kthread_should_stop() is true and lo_bio is NULL, we are
+ * done with the loop.
*/
static int loop_thread(void *data)
{
struct loop_device *lo = data;
struct bio *bio;
- daemonize("loop%d", lo->lo_number);
-
/*
* loop can be used in an encrypted device,
* hence, it mustn't be stopped at all
@@ -587,47 +589,21 @@ static int loop_thread(void *data)
set_user_nice(current, -20);
- lo->lo_state = Lo_bound;
- lo->lo_pending = 1;
+ while (!kthread_should_stop() || lo->lo_bio) {
- /*
- * complete it, we are running
- */
- complete(&lo->lo_done);
-
- for (;;) {
- int pending;
+ wait_event_interruptible(lo->lo_event,
+ lo->lo_bio || kthread_should_stop());
- if (wait_for_completion_interruptible(&lo->lo_bh_done))
+ if (!lo->lo_bio)
continue;
-
spin_lock_irq(&lo->lo_lock);
-
- /*
- * could be completed because of tear-down, not pending work
- */
- if (unlikely(!lo->lo_pending)) {
- spin_unlock_irq(&lo->lo_lock);
- break;
- }
-
bio = loop_get_bio(lo);
- lo->lo_pending--;
- pending = lo->lo_pending;
spin_unlock_irq(&lo->lo_lock);
BUG_ON(!bio);
loop_handle_bio(lo, bio);
-
- /*
- * upped both for pending work and tear-down, lo_pending
- * will hit zero then
- */
- if (unlikely(!pending))
- break;
}
- complete(&lo->lo_done);
return 0;
}
@@ -662,7 +638,8 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
lo->lo_backing_file = file;
- lo->lo_blocksize = mapping->host->i_blksize;
+ lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ?
+ mapping->host->i_bdev->bd_block_size : PAGE_SIZE;
lo->old_gfp_mask = mapping_gfp_mask(mapping);
mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
complete(&p->wait);
@@ -794,7 +771,9 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)
lo_flags |= LO_FLAGS_READ_ONLY;
- lo_blocksize = inode->i_blksize;
+ lo_blocksize = S_ISBLK(inode->i_mode) ?
+ inode->i_bdev->bd_block_size : PAGE_SIZE;
+
error = 0;
} else {
goto out_putf;
@@ -837,12 +816,26 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
set_blocksize(bdev, lo_blocksize);
- error = kernel_thread(loop_thread, lo, CLONE_KERNEL);
- if (error < 0)
- goto out_putf;
- wait_for_completion(&lo->lo_done);
+ lo->lo_thread = kthread_create(loop_thread, lo, "loop%d",
+ lo->lo_number);
+ if (IS_ERR(lo->lo_thread)) {
+ error = PTR_ERR(lo->lo_thread);
+ goto out_clr;
+ }
+ lo->lo_state = Lo_bound;
+ wake_up_process(lo->lo_thread);
return 0;
+out_clr:
+ lo->lo_thread = NULL;
+ lo->lo_device = NULL;
+ lo->lo_backing_file = NULL;
+ lo->lo_flags = 0;
+ set_capacity(disks[lo->lo_number], 0);
+ invalidate_bdev(bdev, 0);
+ bd_set_size(bdev, 0);
+ mapping_set_gfp_mask(mapping, lo->old_gfp_mask);
+ lo->lo_state = Lo_unbound;
out_putf:
fput(file);
out:
@@ -904,12 +897,9 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
spin_lock_irq(&lo->lo_lock);
lo->lo_state = Lo_rundown;
- lo->lo_pending--;
- if (!lo->lo_pending)
- complete(&lo->lo_bh_done);
spin_unlock_irq(&lo->lo_lock);
- wait_for_completion(&lo->lo_done);
+ kthread_stop(lo->lo_thread);
lo->lo_backing_file = NULL;
@@ -922,6 +912,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
lo->lo_sizelimit = 0;
lo->lo_encrypt_key_size = 0;
lo->lo_flags = 0;
+ lo->lo_thread = NULL;
memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
memset(lo->lo_file_name, 0, LO_NAME_SIZE);
@@ -1284,9 +1275,9 @@ static int __init loop_init(void)
if (!lo->lo_queue)
goto out_mem4;
mutex_init(&lo->lo_ctl_mutex);
- init_completion(&lo->lo_done);
- init_completion(&lo->lo_bh_done);
lo->lo_number = i;
+ lo->lo_thread = NULL;
+ init_waitqueue_head(&lo->lo_event);
spin_lock_init(&lo->lo_lock);
disk->major = LOOP_MAJOR;
disk->first_minor = i;
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index d62b49fbf10..45a8f402b07 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -358,7 +358,7 @@ static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
struct ub_scsi_cmd *cmd, struct ub_request *urq);
static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_end_rq(struct request *rq, int uptodate);
+static void ub_end_rq(struct request *rq, unsigned int status);
static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
struct ub_request *urq, struct ub_scsi_cmd *cmd);
static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
@@ -639,9 +639,15 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
struct ub_request *urq;
int n_elem;
- if (atomic_read(&sc->poison) || lun->changed) {
+ if (atomic_read(&sc->poison)) {
+ blkdev_dequeue_request(rq);
+ ub_end_rq(rq, DID_NO_CONNECT << 16);
+ return 0;
+ }
+
+ if (lun->changed && !blk_pc_request(rq)) {
blkdev_dequeue_request(rq);
- ub_end_rq(rq, 0);
+ ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
return 0;
}
@@ -693,7 +699,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
drop:
ub_put_cmd(lun, cmd);
- ub_end_rq(rq, 0);
+ ub_end_rq(rq, DID_ERROR << 16);
return 0;
}
@@ -761,47 +767,53 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
struct ub_lun *lun = cmd->lun;
struct ub_request *urq = cmd->back;
struct request *rq;
- int uptodate;
+ unsigned int scsi_status;
rq = urq->rq;
if (cmd->error == 0) {
- uptodate = 1;
-
if (blk_pc_request(rq)) {
if (cmd->act_len >= rq->data_len)
rq->data_len = 0;
else
rq->data_len -= cmd->act_len;
}
+ scsi_status = 0;
} else {
- uptodate = 0;
-
if (blk_pc_request(rq)) {
/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
rq->sense_len = UB_SENSE_SIZE;
if (sc->top_sense[0] != 0)
- rq->errors = SAM_STAT_CHECK_CONDITION;
+ scsi_status = SAM_STAT_CHECK_CONDITION;
else
- rq->errors = DID_ERROR << 16;
+ scsi_status = DID_ERROR << 16;
} else {
if (cmd->error == -EIO) {
if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
return;
}
+ scsi_status = SAM_STAT_CHECK_CONDITION;
}
}
urq->rq = NULL;
ub_put_cmd(lun, cmd);
- ub_end_rq(rq, uptodate);
+ ub_end_rq(rq, scsi_status);
blk_start_queue(lun->disk->queue);
}
-static void ub_end_rq(struct request *rq, int uptodate)
+static void ub_end_rq(struct request *rq, unsigned int scsi_status)
{
+ int uptodate;
+
+ if (scsi_status == 0) {
+ uptodate = 1;
+ } else {
+ uptodate = 0;
+ rq->errors = scsi_status;
+ }
end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
end_that_request_last(rq, uptodate);
}
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 23f96213f4a..efcc28ec9d9 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -2,7 +2,7 @@
*
* AVM BlueFRITZ! USB driver
*
- * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2003-2006 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -59,7 +59,6 @@ static struct usb_device_id bfusb_table[] = {
MODULE_DEVICE_TABLE(usb, bfusb_table);
-
#define BFUSB_MAX_BLOCK_SIZE 256
#define BFUSB_BLOCK_TIMEOUT 3000
@@ -70,7 +69,7 @@ MODULE_DEVICE_TABLE(usb, bfusb_table);
#define BFUSB_MAX_BULK_TX 2
#define BFUSB_MAX_BULK_RX 2
-struct bfusb {
+struct bfusb_data {
struct hci_dev *hdev;
unsigned long state;
@@ -92,137 +91,136 @@ struct bfusb {
struct sk_buff_head completed_q;
};
-struct bfusb_scb {
+struct bfusb_data_scb {
struct urb *urb;
};
static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs);
static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs);
-static struct urb *bfusb_get_completed(struct bfusb *bfusb)
+static struct urb *bfusb_get_completed(struct bfusb_data *data)
{
struct sk_buff *skb;
struct urb *urb = NULL;
- BT_DBG("bfusb %p", bfusb);
+ BT_DBG("bfusb %p", data);
- skb = skb_dequeue(&bfusb->completed_q);
+ skb = skb_dequeue(&data->completed_q);
if (skb) {
- urb = ((struct bfusb_scb *) skb->cb)->urb;
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
kfree_skb(skb);
}
return urb;
}
-static void bfusb_unlink_urbs(struct bfusb *bfusb)
+static void bfusb_unlink_urbs(struct bfusb_data *data)
{
struct sk_buff *skb;
struct urb *urb;
- BT_DBG("bfusb %p", bfusb);
+ BT_DBG("bfusb %p", data);
- while ((skb = skb_dequeue(&bfusb->pending_q))) {
- urb = ((struct bfusb_scb *) skb->cb)->urb;
+ while ((skb = skb_dequeue(&data->pending_q))) {
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
usb_kill_urb(urb);
- skb_queue_tail(&bfusb->completed_q, skb);
+ skb_queue_tail(&data->completed_q, skb);
}
- while ((urb = bfusb_get_completed(bfusb)))
+ while ((urb = bfusb_get_completed(data)))
usb_free_urb(urb);
}
-
-static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
+static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
{
- struct bfusb_scb *scb = (void *) skb->cb;
- struct urb *urb = bfusb_get_completed(bfusb);
+ struct bfusb_data_scb *scb = (void *) skb->cb;
+ struct urb *urb = bfusb_get_completed(data);
int err, pipe;
- BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
+ BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
return -ENOMEM;
- pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
- usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, skb->len,
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len,
bfusb_tx_complete, skb);
scb->urb = urb;
- skb_queue_tail(&bfusb->pending_q, skb);
+ skb_queue_tail(&data->pending_q, skb);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk tx submit failed urb %p err %d",
- bfusb->hdev->name, urb, err);
- skb_unlink(skb, &bfusb->pending_q);
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
usb_free_urb(urb);
} else
- atomic_inc(&bfusb->pending_tx);
+ atomic_inc(&data->pending_tx);
return err;
}
-static void bfusb_tx_wakeup(struct bfusb *bfusb)
+static void bfusb_tx_wakeup(struct bfusb_data *data)
{
struct sk_buff *skb;
- BT_DBG("bfusb %p", bfusb);
+ BT_DBG("bfusb %p", data);
- if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
- set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+ if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) {
+ set_bit(BFUSB_TX_WAKEUP, &data->state);
return;
}
do {
- clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+ clear_bit(BFUSB_TX_WAKEUP, &data->state);
- while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
- (skb = skb_dequeue(&bfusb->transmit_q))) {
- if (bfusb_send_bulk(bfusb, skb) < 0) {
- skb_queue_head(&bfusb->transmit_q, skb);
+ while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) &&
+ (skb = skb_dequeue(&data->transmit_q))) {
+ if (bfusb_send_bulk(data, skb) < 0) {
+ skb_queue_head(&data->transmit_q, skb);
break;
}
}
- } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
+ } while (test_bit(BFUSB_TX_WAKEUP, &data->state));
- clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
+ clear_bit(BFUSB_TX_PROCESS, &data->state);
}
static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
- struct bfusb *bfusb = (struct bfusb *) skb->dev;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
- BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+ BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
- atomic_dec(&bfusb->pending_tx);
+ atomic_dec(&data->pending_tx);
- if (!test_bit(HCI_RUNNING, &bfusb->hdev->flags))
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
return;
if (!urb->status)
- bfusb->hdev->stat.byte_tx += skb->len;
+ data->hdev->stat.byte_tx += skb->len;
else
- bfusb->hdev->stat.err_tx++;
+ data->hdev->stat.err_tx++;
- read_lock(&bfusb->lock);
+ read_lock(&data->lock);
- skb_unlink(skb, &bfusb->pending_q);
- skb_queue_tail(&bfusb->completed_q, skb);
+ skb_unlink(skb, &data->pending_q);
+ skb_queue_tail(&data->completed_q, skb);
- bfusb_tx_wakeup(bfusb);
+ bfusb_tx_wakeup(data);
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
}
-static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
+static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
{
- struct bfusb_scb *scb;
+ struct bfusb_data_scb *scb;
struct sk_buff *skb;
int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
@@ -231,28 +229,29 @@ static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
return -ENOMEM;
- if (!(skb = bt_skb_alloc(size, GFP_ATOMIC))) {
+ skb = bt_skb_alloc(size, GFP_ATOMIC);
+ if (!skb) {
usb_free_urb(urb);
return -ENOMEM;
}
- skb->dev = (void *) bfusb;
+ skb->dev = (void *) data;
- scb = (struct bfusb_scb *) skb->cb;
+ scb = (struct bfusb_data_scb *) skb->cb;
scb->urb = urb;
- pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
+ pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep);
- usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, size,
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size,
bfusb_rx_complete, skb);
- skb_queue_tail(&bfusb->pending_q, skb);
+ skb_queue_tail(&data->pending_q, skb);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk rx submit failed urb %p err %d",
- bfusb->hdev->name, urb, err);
- skb_unlink(skb, &bfusb->pending_q);
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
kfree_skb(skb);
usb_free_urb(urb);
}
@@ -260,15 +259,15 @@ static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
return err;
}
-static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
+static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
{
- BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
+ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
if (hdr & 0x10) {
- BT_ERR("%s error in block", bfusb->hdev->name);
- if (bfusb->reassembly)
- kfree_skb(bfusb->reassembly);
- bfusb->reassembly = NULL;
+ BT_ERR("%s error in block", data->hdev->name);
+ if (data->reassembly)
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
return -EIO;
}
@@ -277,46 +276,46 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *
unsigned char pkt_type;
int pkt_len = 0;
- if (bfusb->reassembly) {
- BT_ERR("%s unexpected start block", bfusb->hdev->name);
- kfree_skb(bfusb->reassembly);
- bfusb->reassembly = NULL;
+ if (data->reassembly) {
+ BT_ERR("%s unexpected start block", data->hdev->name);
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
}
if (len < 1) {
- BT_ERR("%s no packet type found", bfusb->hdev->name);
+ BT_ERR("%s no packet type found", data->hdev->name);
return -EPROTO;
}
- pkt_type = *data++; len--;
+ pkt_type = *buf++; len--;
switch (pkt_type) {
case HCI_EVENT_PKT:
if (len >= HCI_EVENT_HDR_SIZE) {
- struct hci_event_hdr *hdr = (struct hci_event_hdr *) data;
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
} else {
- BT_ERR("%s event block is too short", bfusb->hdev->name);
+ BT_ERR("%s event block is too short", data->hdev->name);
return -EILSEQ;
}
break;
case HCI_ACLDATA_PKT:
if (len >= HCI_ACL_HDR_SIZE) {
- struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) data;
+ struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
} else {
- BT_ERR("%s data block is too short", bfusb->hdev->name);
+ BT_ERR("%s data block is too short", data->hdev->name);
return -EILSEQ;
}
break;
case HCI_SCODATA_PKT:
if (len >= HCI_SCO_HDR_SIZE) {
- struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) data;
+ struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
} else {
- BT_ERR("%s audio block is too short", bfusb->hdev->name);
+ BT_ERR("%s audio block is too short", data->hdev->name);
return -EILSEQ;
}
break;
@@ -324,27 +323,27 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *
skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
if (!skb) {
- BT_ERR("%s no memory for the packet", bfusb->hdev->name);
+ BT_ERR("%s no memory for the packet", data->hdev->name);
return -ENOMEM;
}
- skb->dev = (void *) bfusb->hdev;
+ skb->dev = (void *) data->hdev;
bt_cb(skb)->pkt_type = pkt_type;
- bfusb->reassembly = skb;
+ data->reassembly = skb;
} else {
- if (!bfusb->reassembly) {
- BT_ERR("%s unexpected continuation block", bfusb->hdev->name);
+ if (!data->reassembly) {
+ BT_ERR("%s unexpected continuation block", data->hdev->name);
return -EIO;
}
}
if (len > 0)
- memcpy(skb_put(bfusb->reassembly, len), data, len);
+ memcpy(skb_put(data->reassembly, len), buf, len);
if (hdr & 0x08) {
- hci_recv_frame(bfusb->reassembly);
- bfusb->reassembly = NULL;
+ hci_recv_frame(data->reassembly);
+ data->reassembly = NULL;
}
return 0;
@@ -353,22 +352,22 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *
static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
- struct bfusb *bfusb = (struct bfusb *) skb->dev;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
unsigned char *buf = urb->transfer_buffer;
int count = urb->actual_length;
int err, hdr, len;
BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
- read_lock(&bfusb->lock);
+ read_lock(&data->lock);
- if (!test_bit(HCI_RUNNING, &bfusb->hdev->flags))
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
goto unlock;
if (urb->status || !count)
goto resubmit;
- bfusb->hdev->stat.byte_rx += count;
+ data->hdev->stat.byte_rx += count;
skb_put(skb, count);
@@ -387,90 +386,89 @@ static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs)
if (count < len) {
BT_ERR("%s block extends over URB buffer ranges",
- bfusb->hdev->name);
+ data->hdev->name);
}
if ((hdr & 0xe1) == 0xc1)
- bfusb_recv_block(bfusb, hdr, buf, len);
+ bfusb_recv_block(data, hdr, buf, len);
count -= len;
buf += len;
}
- skb_unlink(skb, &bfusb->pending_q);
+ skb_unlink(skb, &data->pending_q);
kfree_skb(skb);
- bfusb_rx_submit(bfusb, urb);
+ bfusb_rx_submit(data, urb);
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
return;
resubmit:
- urb->dev = bfusb->udev;
+ urb->dev = data->udev;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk resubmit failed urb %p err %d",
- bfusb->hdev->name, urb, err);
+ data->hdev->name, urb, err);
}
unlock:
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
}
-
static int bfusb_open(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
unsigned long flags;
int i, err;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
return 0;
- write_lock_irqsave(&bfusb->lock, flags);
+ write_lock_irqsave(&data->lock, flags);
- err = bfusb_rx_submit(bfusb, NULL);
+ err = bfusb_rx_submit(data, NULL);
if (!err) {
for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
- bfusb_rx_submit(bfusb, NULL);
+ bfusb_rx_submit(data, NULL);
} else {
clear_bit(HCI_RUNNING, &hdev->flags);
}
- write_unlock_irqrestore(&bfusb->lock, flags);
+ write_unlock_irqrestore(&data->lock, flags);
return err;
}
static int bfusb_flush(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
- skb_queue_purge(&bfusb->transmit_q);
+ skb_queue_purge(&data->transmit_q);
return 0;
}
static int bfusb_close(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
unsigned long flags;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
- write_lock_irqsave(&bfusb->lock, flags);
- write_unlock_irqrestore(&bfusb->lock, flags);
+ write_lock_irqsave(&data->lock, flags);
+ write_unlock_irqrestore(&data->lock, flags);
- bfusb_unlink_urbs(bfusb);
+ bfusb_unlink_urbs(data);
bfusb_flush(hdev);
return 0;
@@ -479,7 +477,7 @@ static int bfusb_close(struct hci_dev *hdev)
static int bfusb_send_frame(struct sk_buff *skb)
{
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
- struct bfusb *bfusb;
+ struct bfusb_data *data;
struct sk_buff *nskb;
unsigned char buf[3];
int sent = 0, size, count;
@@ -494,7 +492,7 @@ static int bfusb_send_frame(struct sk_buff *skb)
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -EBUSY;
- bfusb = (struct bfusb *) hdev->driver_data;
+ data = hdev->driver_data;
switch (bt_cb(skb)->pkt_type) {
case HCI_COMMAND_PKT:
@@ -514,12 +512,13 @@ static int bfusb_send_frame(struct sk_buff *skb)
count = skb->len;
/* Max HCI frame size seems to be 1511 + 1 */
- if (!(nskb = bt_skb_alloc(count + 32, GFP_ATOMIC))) {
+ nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
+ if (!nskb) {
BT_ERR("Can't allocate memory for new packet");
return -ENOMEM;
}
- nskb->dev = (void *) bfusb;
+ nskb->dev = (void *) data;
while (count) {
size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
@@ -536,18 +535,18 @@ static int bfusb_send_frame(struct sk_buff *skb)
}
/* Don't send frame with multiple size of bulk max packet */
- if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
+ if ((nskb->len % data->bulk_pkt_size) == 0) {
buf[0] = 0xdd;
buf[1] = 0x00;
memcpy(skb_put(nskb, 2), buf, 2);
}
- read_lock(&bfusb->lock);
+ read_lock(&data->lock);
- skb_queue_tail(&bfusb->transmit_q, nskb);
- bfusb_tx_wakeup(bfusb);
+ skb_queue_tail(&data->transmit_q, nskb);
+ bfusb_tx_wakeup(data);
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
kfree_skb(skb);
@@ -556,11 +555,11 @@ static int bfusb_send_frame(struct sk_buff *skb)
static void bfusb_destruct(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
- kfree(bfusb);
+ kfree(data);
}
static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
@@ -568,25 +567,24 @@ static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg
return -ENOIOCTLCMD;
}
-
-static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
+static int bfusb_load_firmware(struct bfusb_data *data, unsigned char *firmware, int count)
{
unsigned char *buf;
int err, pipe, len, size, sent = 0;
- BT_DBG("bfusb %p udev %p", bfusb, bfusb->udev);
+ BT_DBG("bfusb %p udev %p", data, data->udev);
BT_INFO("BlueFRITZ! USB loading firmware");
- pipe = usb_sndctrlpipe(bfusb->udev, 0);
+ pipe = usb_sndctrlpipe(data->udev, 0);
- if (usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
BT_ERR("Can't change to loading configuration");
return -EBUSY;
}
- bfusb->udev->toggle[0] = bfusb->udev->toggle[1] = 0;
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
if (!buf) {
@@ -594,14 +592,14 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int
return -ENOMEM;
}
- pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
while (count) {
size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
memcpy(buf, firmware + sent, size);
- err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
+ err = usb_bulk_msg(data->udev, pipe, buf, size,
&len, BFUSB_BLOCK_TIMEOUT);
if (err || (len != size)) {
@@ -613,21 +611,23 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int
count -= size;
}
- if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
- &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
+ err = usb_bulk_msg(data->udev, pipe, NULL, 0,
+ &len, BFUSB_BLOCK_TIMEOUT);
+ if (err < 0) {
BT_ERR("Error in null packet request");
goto error;
}
- pipe = usb_sndctrlpipe(bfusb->udev, 0);
+ pipe = usb_sndctrlpipe(data->udev, 0);
- if ((err = usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
- 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) {
+ err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
BT_ERR("Can't change to running configuration");
goto error;
}
- bfusb->udev->toggle[0] = bfusb->udev->toggle[1] = 0;
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
BT_INFO("BlueFRITZ! USB device ready");
@@ -637,9 +637,9 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int
error:
kfree(buf);
- pipe = usb_sndctrlpipe(bfusb->udev, 0);
+ pipe = usb_sndctrlpipe(data->udev, 0);
- usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
return err;
@@ -652,7 +652,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
struct usb_host_endpoint *bulk_out_ep;
struct usb_host_endpoint *bulk_in_ep;
struct hci_dev *hdev;
- struct bfusb *bfusb;
+ struct bfusb_data *data;
BT_DBG("intf %p id %p", intf, id);
@@ -672,23 +672,24 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
}
/* Initialize control structure and load firmware */
- if (!(bfusb = kzalloc(sizeof(struct bfusb), GFP_KERNEL))) {
+ data = kzalloc(sizeof(struct bfusb_data), GFP_KERNEL);
+ if (!data) {
BT_ERR("Can't allocate memory for control structure");
goto done;
}
- bfusb->udev = udev;
- bfusb->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
- bfusb->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
- bfusb->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
+ data->udev = udev;
+ data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
+ data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
+ data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
- rwlock_init(&bfusb->lock);
+ rwlock_init(&data->lock);
- bfusb->reassembly = NULL;
+ data->reassembly = NULL;
- skb_queue_head_init(&bfusb->transmit_q);
- skb_queue_head_init(&bfusb->pending_q);
- skb_queue_head_init(&bfusb->completed_q);
+ skb_queue_head_init(&data->transmit_q);
+ skb_queue_head_init(&data->pending_q);
+ skb_queue_head_init(&data->completed_q);
if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
BT_ERR("Firmware request failed");
@@ -697,7 +698,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
- if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
+ if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
BT_ERR("Firmware loading failed");
goto release;
}
@@ -711,10 +712,10 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
goto error;
}
- bfusb->hdev = hdev;
+ data->hdev = hdev;
hdev->type = HCI_USB;
- hdev->driver_data = bfusb;
+ hdev->driver_data = data;
SET_HCIDEV_DEV(hdev, &intf->dev);
hdev->open = bfusb_open;
@@ -732,7 +733,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
goto error;
}
- usb_set_intfdata(intf, bfusb);
+ usb_set_intfdata(intf, data);
return 0;
@@ -740,7 +741,7 @@ release:
release_firmware(firmware);
error:
- kfree(bfusb);
+ kfree(data);
done:
return -EIO;
@@ -748,8 +749,8 @@ done:
static void bfusb_disconnect(struct usb_interface *intf)
{
- struct bfusb *bfusb = usb_get_intfdata(intf);
- struct hci_dev *hdev = bfusb->hdev;
+ struct bfusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
BT_DBG("intf %p", intf);
@@ -779,7 +780,8 @@ static int __init bfusb_init(void)
BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
- if ((err = usb_register(&bfusb_driver)) < 0)
+ err = usb_register(&bfusb_driver);
+ if (err < 0)
BT_ERR("Failed to register BlueFRITZ! USB driver");
return err;
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 93ba25b7ea3..420b645c4c9 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -241,15 +241,11 @@ static int hci_uart_send_frame(struct sk_buff *skb)
static void hci_uart_destruct(struct hci_dev *hdev)
{
- struct hci_uart *hu;
-
if (!hdev)
return;
BT_DBG("%s", hdev->name);
-
- hu = (struct hci_uart *) hdev->driver_data;
- kfree(hu);
+ kfree(hdev->driver_data);
}
/* ------ LDISC part ------ */
@@ -272,7 +268,7 @@ static int hci_uart_tty_open(struct tty_struct *tty)
return -EEXIST;
if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
- BT_ERR("Can't allocate controll structure");
+ BT_ERR("Can't allocate control structure");
return -ENFILE;
}
@@ -360,7 +356,7 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
*
* Return Value: None
*/
-static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
{
struct hci_uart *hu = (void *)tty->disc_data;
@@ -375,7 +371,8 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char
hu->hdev->stat.byte_rx += count;
spin_unlock(&hu->rx_lock);
- if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver->unthrottle)
+ if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
+ tty->driver->unthrottle)
tty->driver->unthrottle(tty);
}
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
index e2d4beac742..0801af4ad2b 100644
--- a/drivers/bluetooth/hci_usb.c
+++ b/drivers/bluetooth/hci_usb.c
@@ -96,6 +96,9 @@ static struct usb_device_id bluetooth_ids[] = {
/* Ericsson with non-standard id */
{ USB_DEVICE(0x0bdb, 0x1002) },
+ /* Canyon CN-BTU1 with HID interfaces */
+ { USB_DEVICE(0x0c10, 0x0000), .driver_info = HCI_RESET },
+
{ } /* Terminating entry */
};
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index aac67a3a601..a278d98a915 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -2,9 +2,9 @@
*
* Bluetooth virtual HCI driver
*
- * Copyright (C) 2000-2001 Qualcomm Incorporated
- * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
- * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -72,21 +72,21 @@ static int vhci_open_dev(struct hci_dev *hdev)
static int vhci_close_dev(struct hci_dev *hdev)
{
- struct vhci_data *vhci = hdev->driver_data;
+ struct vhci_data *data = hdev->driver_data;
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
- skb_queue_purge(&vhci->readq);
+ skb_queue_purge(&data->readq);
return 0;
}
static int vhci_flush(struct hci_dev *hdev)
{
- struct vhci_data *vhci = hdev->driver_data;
+ struct vhci_data *data = hdev->driver_data;
- skb_queue_purge(&vhci->readq);
+ skb_queue_purge(&data->readq);
return 0;
}
@@ -94,7 +94,7 @@ static int vhci_flush(struct hci_dev *hdev)
static int vhci_send_frame(struct sk_buff *skb)
{
struct hci_dev* hdev = (struct hci_dev *) skb->dev;
- struct vhci_data *vhci;
+ struct vhci_data *data;
if (!hdev) {
BT_ERR("Frame for unknown HCI device (hdev=NULL)");
@@ -104,15 +104,15 @@ static int vhci_send_frame(struct sk_buff *skb)
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -EBUSY;
- vhci = hdev->driver_data;
+ data = hdev->driver_data;
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
- skb_queue_tail(&vhci->readq, skb);
+ skb_queue_tail(&data->readq, skb);
- if (vhci->flags & VHCI_FASYNC)
- kill_fasync(&vhci->fasync, SIGIO, POLL_IN);
+ if (data->flags & VHCI_FASYNC)
+ kill_fasync(&data->fasync, SIGIO, POLL_IN);
- wake_up_interruptible(&vhci->read_wait);
+ wake_up_interruptible(&data->read_wait);
return 0;
}
@@ -122,7 +122,7 @@ static void vhci_destruct(struct hci_dev *hdev)
kfree(hdev->driver_data);
}
-static inline ssize_t vhci_get_user(struct vhci_data *vhci,
+static inline ssize_t vhci_get_user(struct vhci_data *data,
const char __user *buf, size_t count)
{
struct sk_buff *skb;
@@ -139,7 +139,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *vhci,
return -EFAULT;
}
- skb->dev = (void *) vhci->hdev;
+ skb->dev = (void *) data->hdev;
bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
skb_pull(skb, 1);
@@ -148,7 +148,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *vhci,
return count;
}
-static inline ssize_t vhci_put_user(struct vhci_data *vhci,
+static inline ssize_t vhci_put_user(struct vhci_data *data,
struct sk_buff *skb, char __user *buf, int count)
{
char __user *ptr = buf;
@@ -161,42 +161,43 @@ static inline ssize_t vhci_put_user(struct vhci_data *vhci,
total += len;
- vhci->hdev->stat.byte_tx += len;
+ data->hdev->stat.byte_tx += len;
switch (bt_cb(skb)->pkt_type) {
case HCI_COMMAND_PKT:
- vhci->hdev->stat.cmd_tx++;
+ data->hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
- vhci->hdev->stat.acl_tx++;
+ data->hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
- vhci->hdev->stat.cmd_tx++;
+ data->hdev->stat.cmd_tx++;
break;
};
return total;
}
-static loff_t vhci_llseek(struct file * file, loff_t offset, int origin)
+static loff_t vhci_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
-static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, loff_t *pos)
+static ssize_t vhci_read(struct file *file,
+ char __user *buf, size_t count, loff_t *pos)
{
DECLARE_WAITQUEUE(wait, current);
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
struct sk_buff *skb;
ssize_t ret = 0;
- add_wait_queue(&vhci->read_wait, &wait);
+ add_wait_queue(&data->read_wait, &wait);
while (count) {
set_current_state(TASK_INTERRUPTIBLE);
- skb = skb_dequeue(&vhci->readq);
+ skb = skb_dequeue(&data->readq);
if (!skb) {
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
@@ -213,7 +214,7 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo
}
if (access_ok(VERIFY_WRITE, buf, count))
- ret = vhci_put_user(vhci, skb, buf, count);
+ ret = vhci_put_user(data, skb, buf, count);
else
ret = -EFAULT;
@@ -221,7 +222,7 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo
break;
}
set_current_state(TASK_RUNNING);
- remove_wait_queue(&vhci->read_wait, &wait);
+ remove_wait_queue(&data->read_wait, &wait);
return ret;
}
@@ -229,21 +230,21 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo
static ssize_t vhci_write(struct file *file,
const char __user *buf, size_t count, loff_t *pos)
{
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
- return vhci_get_user(vhci, buf, count);
+ return vhci_get_user(data, buf, count);
}
static unsigned int vhci_poll(struct file *file, poll_table *wait)
{
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
- poll_wait(file, &vhci->read_wait, wait);
+ poll_wait(file, &data->read_wait, wait);
- if (!skb_queue_empty(&vhci->readq))
+ if (!skb_queue_empty(&data->readq))
return POLLIN | POLLRDNORM;
return POLLOUT | POLLWRNORM;
@@ -257,26 +258,26 @@ static int vhci_ioctl(struct inode *inode, struct file *file,
static int vhci_open(struct inode *inode, struct file *file)
{
- struct vhci_data *vhci;
+ struct vhci_data *data;
struct hci_dev *hdev;
- vhci = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
- if (!vhci)
+ data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
+ if (!data)
return -ENOMEM;
- skb_queue_head_init(&vhci->readq);
- init_waitqueue_head(&vhci->read_wait);
+ skb_queue_head_init(&data->readq);
+ init_waitqueue_head(&data->read_wait);
hdev = hci_alloc_dev();
if (!hdev) {
- kfree(vhci);
+ kfree(data);
return -ENOMEM;
}
- vhci->hdev = hdev;
+ data->hdev = hdev;
- hdev->type = HCI_VHCI;
- hdev->driver_data = vhci;
+ hdev->type = HCI_VIRTUAL;
+ hdev->driver_data = data;
hdev->open = vhci_open_dev;
hdev->close = vhci_close_dev;
@@ -288,20 +289,20 @@ static int vhci_open(struct inode *inode, struct file *file)
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
- kfree(vhci);
+ kfree(data);
hci_free_dev(hdev);
return -EBUSY;
}
- file->private_data = vhci;
+ file->private_data = data;
return nonseekable_open(inode, file);
}
static int vhci_release(struct inode *inode, struct file *file)
{
- struct vhci_data *vhci = file->private_data;
- struct hci_dev *hdev = vhci->hdev;
+ struct vhci_data *data = file->private_data;
+ struct hci_dev *hdev = data->hdev;
if (hci_unregister_dev(hdev) < 0) {
BT_ERR("Can't unregister HCI device %s", hdev->name);
@@ -316,17 +317,17 @@ static int vhci_release(struct inode *inode, struct file *file)
static int vhci_fasync(int fd, struct file *file, int on)
{
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
int err;
- err = fasync_helper(fd, file, on, &vhci->fasync);
+ err = fasync_helper(fd, file, on, &data->fasync);
if (err < 0)
return err;
if (on)
- vhci->flags |= VHCI_FASYNC;
+ data->flags |= VHCI_FASYNC;
else
- vhci->flags &= ~VHCI_FASYNC;
+ data->flags &= ~VHCI_FASYNC;
return 0;
}
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 52ea94b891f..4cc619edf42 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -439,6 +439,14 @@ config SGI_MBCS
If you have an SGI Altix with an attached SABrick
say Y or M here, otherwise say N.
+config MSPEC
+ tristate "Memory special operations driver"
+ depends on IA64
+ help
+ If you have an ia64 and you want to enable memory special
+ operations support (formerly known as fetchop), say Y here,
+ otherwise say N.
+
source "drivers/serial/Kconfig"
config UNIX98_PTYS
@@ -739,7 +747,7 @@ config NVRAM
config RTC
tristate "Enhanced Real Time Clock Support"
- depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM
+ depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM && !SUPERH
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -823,14 +831,6 @@ config DS1302
will get access to the real time clock (or hardware clock) built
into your computer.
-config S3C2410_RTC
- bool "S3C2410 RTC Driver"
- depends on ARCH_S3C2410
- help
- RTC (Realtime Clock) driver for the clock inbuilt into the
- Samsung S3C2410. This can provide periodic interrupt rates
- from 1Hz to 64Hz for user programs, and wakeup from Alarm.
-
config COBALT_LCD
bool "Support for Cobalt LCD"
depends on MIPS_COBALT
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 8c6dfc62152..19114df59bb 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
+obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_MMTIMER) += mmtimer.o
obj-$(CONFIG_VIOCONS) += viocons.o
obj-$(CONFIG_VIOTAPE) += viotape.o
@@ -68,7 +69,6 @@ obj-$(CONFIG_EFI_RTC) += efirtc.o
obj-$(CONFIG_SGI_DS1286) += ds1286.o
obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o
obj-$(CONFIG_DS1302) += ds1302.o
-obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o
ifeq ($(CONFIG_GENERIC_NVRAM),y)
obj-$(CONFIG_NVRAM) += generic_nvram.o
else
diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c
index 5e59c0b4273..4711d9b3a59 100644
--- a/drivers/char/generic_serial.c
+++ b/drivers/char/generic_serial.c
@@ -746,11 +746,9 @@ void gs_set_termios (struct tty_struct * tty,
gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp);
}
-#if 0
/* This is an optimization that is only allowed for dumb cards */
/* Smart cards require knowledge of iflags and oflags too: that
might change hardware cooking mode.... */
-#endif
if (old_termios) {
if( (tiosp->c_iflag == old_termios->c_iflag)
&& (tiosp->c_oflag == old_termios->c_oflag)
@@ -774,14 +772,7 @@ void gs_set_termios (struct tty_struct * tty,
if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n");
}
- baudrate = tiosp->c_cflag & CBAUD;
- if (baudrate & CBAUDEX) {
- baudrate &= ~CBAUDEX;
- if ((baudrate < 1) || (baudrate > 4))
- tiosp->c_cflag &= ~CBAUDEX;
- else
- baudrate += 15;
- }
+ baudrate = tty_get_baud_rate(tty);
baudrate = gs_baudrates[baudrate];
if ((tiosp->c_cflag & CBAUD) == B38400) {
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 8afba339f05..58b0eb58111 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -868,8 +868,8 @@ int hpet_alloc(struct hpet_data *hdp)
do_div(temp, period);
hpetp->hp_tick_freq = temp; /* ticks per second */
- printk(KERN_INFO "hpet%d: at MMIO 0x%lx (virtual 0x%p), IRQ%s",
- hpetp->hp_which, hdp->hd_phys_address, hdp->hd_address,
+ printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s",
+ hpetp->hp_which, hdp->hd_phys_address,
hpetp->hp_ntimer > 1 ? "s" : "");
for (i = 0; i < hpetp->hp_ntimer; i++)
printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c
index ccd7e710223..8efbc9c0e54 100644
--- a/drivers/char/hw_random/intel-rng.c
+++ b/drivers/char/hw_random/intel-rng.c
@@ -50,6 +50,43 @@
#define INTEL_RNG_ADDR_LEN 3
/*
+ * LPC bridge PCI config space registers
+ */
+#define FWH_DEC_EN1_REG_OLD 0xe3
+#define FWH_DEC_EN1_REG_NEW 0xd9 /* high byte of 16-bit register */
+#define FWH_F8_EN_MASK 0x80
+
+#define BIOS_CNTL_REG_OLD 0x4e
+#define BIOS_CNTL_REG_NEW 0xdc
+#define BIOS_CNTL_WRITE_ENABLE_MASK 0x01
+#define BIOS_CNTL_LOCK_ENABLE_MASK 0x02
+
+/*
+ * Magic address at which Intel Firmware Hubs get accessed
+ */
+#define INTEL_FWH_ADDR 0xffff0000
+#define INTEL_FWH_ADDR_LEN 2
+
+/*
+ * Intel Firmware Hub command codes (write to any address inside the device)
+ */
+#define INTEL_FWH_RESET_CMD 0xff /* aka READ_ARRAY */
+#define INTEL_FWH_READ_ID_CMD 0x90
+
+/*
+ * Intel Firmware Hub Read ID command result addresses
+ */
+#define INTEL_FWH_MANUFACTURER_CODE_ADDRESS 0x000000
+#define INTEL_FWH_DEVICE_CODE_ADDRESS 0x000001
+
+/*
+ * Intel Firmware Hub Read ID command result values
+ */
+#define INTEL_FWH_MANUFACTURER_CODE 0x89
+#define INTEL_FWH_DEVICE_CODE_8M 0xac
+#define INTEL_FWH_DEVICE_CODE_4M 0xad
+
+/*
* Data for PCI driver interface
*
* This data only exists for exporting the supported
@@ -58,12 +95,50 @@
* want to register another driver on the same PCI id.
*/
static const struct pci_device_id pci_tbl[] = {
- { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
- { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
- { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
- { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
- { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
- { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+/* AA
+ { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AA */
+/* AB
+ { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AB */
+/* ??
+ { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+/* BAM, CAM, DBM, FBM, GxM
+ { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BAM */
+ { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CAM */
+ { 0x8086, 0x24cc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DBM */
+ { 0x8086, 0x2641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* FBM */
+ { 0x8086, 0x27b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM */
+ { 0x8086, 0x27bd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM DH */
+/* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx
+ { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BA */
+ { 0x8086, 0x2480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CA */
+ { 0x8086, 0x24c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DB */
+ { 0x8086, 0x24d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ex */
+ { 0x8086, 0x25a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 6300 */
+ { 0x8086, 0x2640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Fx */
+ { 0x8086, 0x2670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2674, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2675, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2679, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x27b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Gx */
+/* E
+ { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* E */
{ 0, }, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
@@ -138,22 +213,115 @@ static struct hwrng intel_rng = {
};
+#ifdef CONFIG_SMP
+static char __initdata waitflag;
+
+static void __init intel_init_wait(void *unused)
+{
+ while (waitflag)
+ cpu_relax();
+}
+#endif
+
static int __init mod_init(void)
{
int err = -ENODEV;
+ unsigned i;
+ struct pci_dev *dev = NULL;
void __iomem *mem;
- u8 hw_status;
+ unsigned long flags;
+ u8 bios_cntl_off, fwh_dec_en1_off;
+ u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff;
+ u8 hw_status, mfc, dvc;
- if (!pci_dev_present(pci_tbl))
+ for (i = 0; !dev && pci_tbl[i].vendor; ++i)
+ dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, NULL);
+
+ if (!dev)
goto out; /* Device not found. */
+ /* Check for Intel 82802 */
+ if (dev->device < 0x2640) {
+ fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;
+ bios_cntl_off = BIOS_CNTL_REG_OLD;
+ } else {
+ fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;
+ bios_cntl_off = BIOS_CNTL_REG_NEW;
+ }
+
+ pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val);
+ pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val);
+
+ mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);
+ if (mem == NULL) {
+ pci_dev_put(dev);
+ err = -EBUSY;
+ goto out;
+ }
+
+ /*
+ * Since the BIOS code/data is going to disappear from its normal
+ * location with the Read ID command, all activity on the system
+ * must be stopped until the state is back to normal.
+ */
+#ifdef CONFIG_SMP
+ set_mb(waitflag, 1);
+ if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) {
+ set_mb(waitflag, 0);
+ pci_dev_put(dev);
+ printk(KERN_ERR PFX "cannot run on all processors\n");
+ err = -EAGAIN;
+ goto err_unmap;
+ }
+#endif
+ local_irq_save(flags);
+
+ if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
+ pci_write_config_byte(dev,
+ fwh_dec_en1_off,
+ fwh_dec_en1_val | FWH_F8_EN_MASK);
+ if (!(bios_cntl_val &
+ (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
+ pci_write_config_byte(dev,
+ bios_cntl_off,
+ bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK);
+
+ writeb(INTEL_FWH_RESET_CMD, mem);
+ writeb(INTEL_FWH_READ_ID_CMD, mem);
+ mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);
+ dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS);
+ writeb(INTEL_FWH_RESET_CMD, mem);
+
+ if (!(bios_cntl_val &
+ (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
+ pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val);
+ if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
+ pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val);
+
+ local_irq_restore(flags);
+#ifdef CONFIG_SMP
+ /* Tell other CPUs to resume. */
+ set_mb(waitflag, 0);
+#endif
+
+ iounmap(mem);
+ pci_dev_put(dev);
+
+ if (mfc != INTEL_FWH_MANUFACTURER_CODE ||
+ (dvc != INTEL_FWH_DEVICE_CODE_8M &&
+ dvc != INTEL_FWH_DEVICE_CODE_4M)) {
+ printk(KERN_ERR PFX "FWH not detected\n");
+ err = -ENODEV;
+ goto out;
+ }
+
err = -ENOMEM;
mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);
if (!mem)
goto out;
intel_rng.priv = (unsigned long)mem;
- /* Check for Intel 82802 */
+ /* Check for Random Number Generator */
err = -ENODEV;
hw_status = hwstatus_get(mem);
if ((hw_status & INTEL_RNG_PRESENT) == 0)
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 8c09997cc3d..6b4d82a4565 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -612,16 +612,6 @@ MODULE_DEVICE_TABLE(pci, istallion_pci_tbl);
#define MINOR2BRD(min) (((min) & 0xc0) >> 6)
#define MINOR2PORT(min) ((min) & 0x3f)
-/*
- * Define a baud rate table that converts termios baud rate selector
- * into the actual baud rate value. All baud rate calculations are based
- * on the actual baud rate required.
- */
-static unsigned int stli_baudrates[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
-};
-
/*****************************************************************************/
/*
@@ -2747,15 +2737,7 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tio
/*
* Start of by setting the baud, char size, parity and stop bit info.
*/
- pp->baudout = tiosp->c_cflag & CBAUD;
- if (pp->baudout & CBAUDEX) {
- pp->baudout &= ~CBAUDEX;
- if ((pp->baudout < 1) || (pp->baudout > 4))
- tiosp->c_cflag &= ~CBAUDEX;
- else
- pp->baudout += 15;
- }
- pp->baudout = stli_baudrates[pp->baudout];
+ pp->baudout = tty_get_baud_rate(portp->tty);
if ((tiosp->c_cflag & CBAUD) == B38400) {
if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
pp->baudout = 57600;
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index f875fda3b08..1ecea7d448f 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -906,7 +906,7 @@ static int __init lp_init (void)
lp_class = class_create(THIS_MODULE, "printer");
if (IS_ERR(lp_class)) {
err = PTR_ERR(lp_class);
- goto out_devfs;
+ goto out_reg;
}
if (parport_register_driver (&lp_driver)) {
@@ -927,7 +927,7 @@ static int __init lp_init (void)
out_class:
class_destroy(lp_class);
-out_devfs:
+out_reg:
unregister_chrdev(LP_MAJOR, "lp");
return err;
}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 917b2040266..6511012cbdc 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -238,6 +238,32 @@ static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
}
#endif
+#ifndef CONFIG_MMU
+static unsigned long get_unmapped_area_mem(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ if (!valid_mmap_phys_addr_range(pgoff, len))
+ return (unsigned long) -EINVAL;
+ return pgoff;
+}
+
+/* can't do an in-place private mapping if there's no MMU */
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+ return vma->vm_flags & VM_MAYSHARE;
+}
+#else
+#define get_unmapped_area_mem NULL
+
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+ return 1;
+}
+#endif
+
static int mmap_mem(struct file * file, struct vm_area_struct * vma)
{
size_t size = vma->vm_end - vma->vm_start;
@@ -245,6 +271,9 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
return -EINVAL;
+ if (!private_mapping_ok(vma))
+ return -ENOSYS;
+
vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
size,
vma->vm_page_prot);
@@ -522,7 +551,7 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
return virtr + wrote;
}
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
static ssize_t read_port(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
@@ -782,6 +811,7 @@ static const struct file_operations mem_fops = {
.write = write_mem,
.mmap = mmap_mem,
.open = open_mem,
+ .get_unmapped_area = get_unmapped_area_mem,
};
static const struct file_operations kmem_fops = {
@@ -790,6 +820,7 @@ static const struct file_operations kmem_fops = {
.write = write_kmem,
.mmap = mmap_kmem,
.open = open_kmem,
+ .get_unmapped_area = get_unmapped_area_mem,
};
static const struct file_operations null_fops = {
@@ -799,7 +830,7 @@ static const struct file_operations null_fops = {
.splice_write = splice_write_null,
};
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
static const struct file_operations port_fops = {
.llseek = memory_lseek,
.read = read_port,
@@ -815,6 +846,10 @@ static const struct file_operations zero_fops = {
.mmap = mmap_zero,
};
+/*
+ * capabilities for /dev/zero
+ * - permits private mappings, "copies" are taken of the source of zeros
+ */
static struct backing_dev_info zero_bdi = {
.capabilities = BDI_CAP_MAP_COPY,
};
@@ -862,14 +897,18 @@ static int memory_open(struct inode * inode, struct file * filp)
switch (iminor(inode)) {
case 1:
filp->f_op = &mem_fops;
+ filp->f_mapping->backing_dev_info =
+ &directly_mappable_cdev_bdi;
break;
case 2:
filp->f_op = &kmem_fops;
+ filp->f_mapping->backing_dev_info =
+ &directly_mappable_cdev_bdi;
break;
case 3:
filp->f_op = &null_fops;
break;
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
case 4:
filp->f_op = &port_fops;
break;
@@ -916,7 +955,7 @@ static const struct {
{1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},
{2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},
{3, "null", S_IRUGO | S_IWUGO, &null_fops},
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
{4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},
#endif
{5, "zero", S_IRUGO | S_IWUGO, &zero_fops},
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
new file mode 100644
index 00000000000..5426b1e5595
--- /dev/null
+++ b/drivers/char/mspec.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2001-2006 Silicon Graphics, Inc. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+/*
+ * SN Platform Special Memory (mspec) Support
+ *
+ * This driver exports the SN special memory (mspec) facility to user
+ * processes.
+ * There are three types of memory made available thru this driver:
+ * fetchops, uncached and cached.
+ *
+ * Fetchops are atomic memory operations that are implemented in the
+ * memory controller on SGI SN hardware.
+ *
+ * Uncached are used for memory write combining feature of the ia64
+ * cpu.
+ *
+ * Cached are used for areas of memory that are used as cached addresses
+ * on our partition and used as uncached addresses from other partitions.
+ * Due to a design constraint of the SN2 Shub, you can not have processors
+ * on the same FSB perform both a cached and uncached reference to the
+ * same cache line. These special memory cached regions prevent the
+ * kernel from ever dropping in a TLB entry and therefore prevent the
+ * processor from ever speculating a cache line from this page.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/numa.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/atomic.h>
+#include <asm/tlbflush.h>
+#include <asm/uncached.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/mspec.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/io.h>
+#include <asm/sn/bte.h>
+#include <asm/sn/shubio.h>
+
+
+#define FETCHOP_ID "SGI Fetchop,"
+#define CACHED_ID "Cached,"
+#define UNCACHED_ID "Uncached"
+#define REVISION "4.0"
+#define MSPEC_BASENAME "mspec"
+
+/*
+ * Page types allocated by the device.
+ */
+enum {
+ MSPEC_FETCHOP = 1,
+ MSPEC_CACHED,
+ MSPEC_UNCACHED
+};
+
+static int is_sn2;
+
+/*
+ * One of these structures is allocated when an mspec region is mmaped. The
+ * structure is pointed to by the vma->vm_private_data field in the vma struct.
+ * This structure is used to record the addresses of the mspec pages.
+ */
+struct vma_data {
+ atomic_t refcnt; /* Number of vmas sharing the data. */
+ spinlock_t lock; /* Serialize access to the vma. */
+ int count; /* Number of pages allocated. */
+ int type; /* Type of pages allocated. */
+ unsigned long maddr[0]; /* Array of MSPEC addresses. */
+};
+
+/* used on shub2 to clear FOP cache in the HUB */
+static unsigned long scratch_page[MAX_NUMNODES];
+#define SH2_AMO_CACHE_ENTRIES 4
+
+static inline int
+mspec_zero_block(unsigned long addr, int len)
+{
+ int status;
+
+ if (is_sn2) {
+ if (is_shub2()) {
+ int nid;
+ void *p;
+ int i;
+
+ nid = nasid_to_cnodeid(get_node_number(__pa(addr)));
+ p = (void *)TO_AMO(scratch_page[nid]);
+
+ for (i=0; i < SH2_AMO_CACHE_ENTRIES; i++) {
+ FETCHOP_LOAD_OP(p, FETCHOP_LOAD);
+ p += FETCHOP_VAR_SIZE;
+ }
+ }
+
+ status = bte_copy(0, addr & ~__IA64_UNCACHED_OFFSET, len,
+ BTE_WACQUIRE | BTE_ZERO_FILL, NULL);
+ } else {
+ memset((char *) addr, 0, len);
+ status = 0;
+ }
+ return status;
+}
+
+/*
+ * mspec_open
+ *
+ * Called when a device mapping is created by a means other than mmap
+ * (via fork, etc.). Increments the reference count on the underlying
+ * mspec data so it is not freed prematurely.
+ */
+static void
+mspec_open(struct vm_area_struct *vma)
+{
+ struct vma_data *vdata;
+
+ vdata = vma->vm_private_data;
+ atomic_inc(&vdata->refcnt);
+}
+
+/*
+ * mspec_close
+ *
+ * Called when unmapping a device mapping. Frees all mspec pages
+ * belonging to the vma.
+ */
+static void
+mspec_close(struct vm_area_struct *vma)
+{
+ struct vma_data *vdata;
+ int i, pages, result, vdata_size;
+
+ vdata = vma->vm_private_data;
+ if (!atomic_dec_and_test(&vdata->refcnt))
+ return;
+
+ pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
+ for (i = 0; i < pages; i++) {
+ if (vdata->maddr[i] == 0)
+ continue;
+ /*
+ * Clear the page before sticking it back
+ * into the pool.
+ */
+ result = mspec_zero_block(vdata->maddr[i], PAGE_SIZE);
+ if (!result)
+ uncached_free_page(vdata->maddr[i]);
+ else
+ printk(KERN_WARNING "mspec_close(): "
+ "failed to zero page %i\n",
+ result);
+ }
+
+ if (vdata_size <= PAGE_SIZE)
+ kfree(vdata);
+ else
+ vfree(vdata);
+}
+
+
+/*
+ * mspec_nopfn
+ *
+ * Creates a mspec page and maps it to user space.
+ */
+static unsigned long
+mspec_nopfn(struct vm_area_struct *vma, unsigned long address)
+{
+ unsigned long paddr, maddr;
+ unsigned long pfn;
+ int index;
+ struct vma_data *vdata = vma->vm_private_data;
+
+ index = (address - vma->vm_start) >> PAGE_SHIFT;
+ maddr = (volatile unsigned long) vdata->maddr[index];
+ if (maddr == 0) {
+ maddr = uncached_alloc_page(numa_node_id());
+ if (maddr == 0)
+ return NOPFN_OOM;
+
+ spin_lock(&vdata->lock);
+ if (vdata->maddr[index] == 0) {
+ vdata->count++;
+ vdata->maddr[index] = maddr;
+ } else {
+ uncached_free_page(maddr);
+ maddr = vdata->maddr[index];
+ }
+ spin_unlock(&vdata->lock);
+ }
+
+ if (vdata->type == MSPEC_FETCHOP)
+ paddr = TO_AMO(maddr);
+ else
+ paddr = __pa(TO_CAC(maddr));
+
+ pfn = paddr >> PAGE_SHIFT;
+
+ return pfn;
+}
+
+static struct vm_operations_struct mspec_vm_ops = {
+ .open = mspec_open,
+ .close = mspec_close,
+ .nopfn = mspec_nopfn
+};
+
+/*
+ * mspec_mmap
+ *
+ * Called when mmaping the device. Initializes the vma with a fault handler
+ * and private data structure necessary to allocate, track, and free the
+ * underlying pages.
+ */
+static int
+mspec_mmap(struct file *file, struct vm_area_struct *vma, int type)
+{
+ struct vma_data *vdata;
+ int pages, vdata_size;
+
+ if (vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ if ((vma->vm_flags & VM_SHARED) == 0)
+ return -EINVAL;
+
+ if ((vma->vm_flags & VM_WRITE) == 0)
+ return -EPERM;
+
+ pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
+ if (vdata_size <= PAGE_SIZE)
+ vdata = kmalloc(vdata_size, GFP_KERNEL);
+ else
+ vdata = vmalloc(vdata_size);
+ if (!vdata)
+ return -ENOMEM;
+ memset(vdata, 0, vdata_size);
+
+ vdata->type = type;
+ spin_lock_init(&vdata->lock);
+ vdata->refcnt = ATOMIC_INIT(1);
+ vma->vm_private_data = vdata;
+
+ vma->vm_flags |= (VM_IO | VM_LOCKED | VM_RESERVED | VM_PFNMAP);
+ if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED)
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_ops = &mspec_vm_ops;
+
+ return 0;
+}
+
+static int
+fetchop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ return mspec_mmap(file, vma, MSPEC_FETCHOP);
+}
+
+static int
+cached_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ return mspec_mmap(file, vma, MSPEC_CACHED);
+}
+
+static int
+uncached_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ return mspec_mmap(file, vma, MSPEC_UNCACHED);
+}
+
+static struct file_operations fetchop_fops = {
+ .owner = THIS_MODULE,
+ .mmap = fetchop_mmap
+};
+
+static struct miscdevice fetchop_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "sgi_fetchop",
+ .fops = &fetchop_fops
+};
+
+static struct file_operations cached_fops = {
+ .owner = THIS_MODULE,
+ .mmap = cached_mmap
+};
+
+static struct miscdevice cached_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mspec_cached",
+ .fops = &cached_fops
+};
+
+static struct file_operations uncached_fops = {
+ .owner = THIS_MODULE,
+ .mmap = uncached_mmap
+};
+
+static struct miscdevice uncached_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mspec_uncached",
+ .fops = &uncached_fops
+};
+
+/*
+ * mspec_init
+ *
+ * Called at boot time to initialize the mspec facility.
+ */
+static int __init
+mspec_init(void)
+{
+ int ret;
+ int nid;
+
+ /*
+ * The fetchop device only works on SN2 hardware, uncached and cached
+ * memory drivers should both be valid on all ia64 hardware
+ */
+ if (ia64_platform_is("sn2")) {
+ is_sn2 = 1;
+ if (is_shub2()) {
+ ret = -ENOMEM;
+ for_each_online_node(nid) {
+ int actual_nid;
+ int nasid;
+ unsigned long phys;
+
+ scratch_page[nid] = uncached_alloc_page(nid);
+ if (scratch_page[nid] == 0)
+ goto free_scratch_pages;
+ phys = __pa(scratch_page[nid]);
+ nasid = get_node_number(phys);
+ actual_nid = nasid_to_cnodeid(nasid);
+ if (actual_nid != nid)
+ goto free_scratch_pages;
+ }
+ }
+
+ ret = misc_register(&fetchop_miscdev);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: failed to register device %i\n",
+ FETCHOP_ID, ret);
+ goto free_scratch_pages;
+ }
+ }
+ ret = misc_register(&cached_miscdev);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to register device %i\n",
+ CACHED_ID, ret);
+ if (is_sn2)
+ misc_deregister(&fetchop_miscdev);
+ goto free_scratch_pages;
+ }
+ ret = misc_register(&uncached_miscdev);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to register device %i\n",
+ UNCACHED_ID, ret);
+ misc_deregister(&cached_miscdev);
+ if (is_sn2)
+ misc_deregister(&fetchop_miscdev);
+ goto free_scratch_pages;
+ }
+
+ printk(KERN_INFO "%s %s initialized devices: %s %s %s\n",
+ MSPEC_BASENAME, REVISION, is_sn2 ? FETCHOP_ID : "",
+ CACHED_ID, UNCACHED_ID);
+
+ return 0;
+
+ free_scratch_pages:
+ for_each_node(nid) {
+ if (scratch_page[nid] != 0)
+ uncached_free_page(scratch_page[nid]);
+ }
+ return ret;
+}
+
+static void __exit
+mspec_exit(void)
+{
+ int nid;
+
+ misc_deregister(&uncached_miscdev);
+ misc_deregister(&cached_miscdev);
+ if (is_sn2) {
+ misc_deregister(&fetchop_miscdev);
+
+ for_each_node(nid) {
+ if (scratch_page[nid] != 0)
+ uncached_free_page(scratch_page[nid]);
+ }
+ }
+}
+
+module_init(mspec_init);
+module_exit(mspec_exit);
+
+MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>");
+MODULE_DESCRIPTION("Driver for SGI SN special memory operations");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c
index 84e5a68635f..ecfaf180e5b 100644
--- a/drivers/char/pc8736x_gpio.c
+++ b/drivers/char/pc8736x_gpio.c
@@ -188,16 +188,6 @@ static void pc8736x_gpio_set(unsigned minor, int val)
pc8736x_gpio_shadow[port] = val;
}
-static void pc8736x_gpio_set_high(unsigned index)
-{
- pc8736x_gpio_set(index, 1);
-}
-
-static void pc8736x_gpio_set_low(unsigned index)
-{
- pc8736x_gpio_set(index, 0);
-}
-
static int pc8736x_gpio_current(unsigned minor)
{
int port, bit;
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 579868af4a5..c596a08c07b 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -288,31 +288,34 @@ static struct cdev raw_cdev = {
static int __init raw_init(void)
{
dev_t dev = MKDEV(RAW_MAJOR, 0);
+ int ret;
- if (register_chrdev_region(dev, MAX_RAW_MINORS, "raw"))
+ ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw");
+ if (ret)
goto error;
cdev_init(&raw_cdev, &raw_fops);
- if (cdev_add(&raw_cdev, dev, MAX_RAW_MINORS)) {
+ ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS);
+ if (ret) {
kobject_put(&raw_cdev.kobj);
- unregister_chrdev_region(dev, MAX_RAW_MINORS);
- goto error;
+ goto error_region;
}
raw_class = class_create(THIS_MODULE, "raw");
if (IS_ERR(raw_class)) {
printk(KERN_ERR "Error creating raw class.\n");
cdev_del(&raw_cdev);
- unregister_chrdev_region(dev, MAX_RAW_MINORS);
- goto error;
+ ret = PTR_ERR(raw_class);
+ goto error_region;
}
class_device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
return 0;
+error_region:
+ unregister_chrdev_region(dev, MAX_RAW_MINORS);
error:
- printk(KERN_ERR "error register raw device\n");
- return 1;
+ return ret;
}
static void __exit raw_exit(void)
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index ab6429b4a84..656f8c0ca52 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -1262,10 +1262,8 @@ void rtc_get_rtc_time(struct rtc_time *rtc_tm)
* Once the read clears, read the RTC time (again via ioctl). Easy.
*/
- while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100) {
- barrier();
+ while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100)
cpu_relax();
- }
/*
* Only the values that we read from the RTC are set. We leave
diff --git a/drivers/char/s3c2410-rtc.c b/drivers/char/s3c2410-rtc.c
deleted file mode 100644
index 5458ef1634e..00000000000
--- a/drivers/char/s3c2410-rtc.c
+++ /dev/null
@@ -1,591 +0,0 @@
-/* drivers/char/s3c2410_rtc.c
- *
- * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>
- * http://www.simtec.co.uk/products/SWLINUX/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * S3C2410 Internal RTC Driver
- *
- * Changelog:
- * 08-Nov-2004 BJD Initial creation
- * 12-Nov-2004 BJD Added periodic IRQ and PM code
- * 22-Nov-2004 BJD Sign-test on alarm code to check for <0
- * 10-Mar-2005 LCVR Changed S3C2410_VA_RTC to S3C24XX_VA_RTC
-*/
-
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/rtc.h>
-#include <linux/bcd.h>
-#include <linux/clk.h>
-
-#include <asm/hardware.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/rtc.h>
-
-#include <asm/mach/time.h>
-
-#include <asm/arch/regs-rtc.h>
-
-/* need this for the RTC_AF definitions */
-#include <linux/mc146818rtc.h>
-
-#undef S3C24XX_VA_RTC
-#define S3C24XX_VA_RTC s3c2410_rtc_base
-
-static struct resource *s3c2410_rtc_mem;
-
-static void __iomem *s3c2410_rtc_base;
-static int s3c2410_rtc_alarmno = NO_IRQ;
-static int s3c2410_rtc_tickno = NO_IRQ;
-static int s3c2410_rtc_freq = 1;
-
-static DEFINE_SPINLOCK(s3c2410_rtc_pie_lock);
-
-/* IRQ Handlers */
-
-static irqreturn_t s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r)
-{
- rtc_update(1, RTC_AF | RTC_IRQF);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r)
-{
- rtc_update(1, RTC_PF | RTC_IRQF);
- return IRQ_HANDLED;
-}
-
-/* Update control registers */
-static void s3c2410_rtc_setaie(int to)
-{
- unsigned int tmp;
-
- pr_debug("%s: aie=%d\n", __FUNCTION__, to);
-
- tmp = readb(S3C2410_RTCALM);
-
- if (to)
- tmp |= S3C2410_RTCALM_ALMEN;
- else
- tmp &= ~S3C2410_RTCALM_ALMEN;
-
-
- writeb(tmp, S3C2410_RTCALM);
-}
-
-static void s3c2410_rtc_setpie(int to)
-{
- unsigned int tmp;
-
- pr_debug("%s: pie=%d\n", __FUNCTION__, to);
-
- spin_lock_irq(&s3c2410_rtc_pie_lock);
- tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
-
- if (to)
- tmp |= S3C2410_TICNT_ENABLE;
-
- writeb(tmp, S3C2410_TICNT);
- spin_unlock_irq(&s3c2410_rtc_pie_lock);
-}
-
-static void s3c2410_rtc_setfreq(int freq)
-{
- unsigned int tmp;
-
- spin_lock_irq(&s3c2410_rtc_pie_lock);
- tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
-
- s3c2410_rtc_freq = freq;
-
- tmp |= (128 / freq)-1;
-
- writeb(tmp, S3C2410_TICNT);
- spin_unlock_irq(&s3c2410_rtc_pie_lock);
-}
-
-/* Time read/write */
-
-static int s3c2410_rtc_gettime(struct rtc_time *rtc_tm)
-{
- unsigned int have_retried = 0;
-
- retry_get_time:
- rtc_tm->tm_min = readb(S3C2410_RTCMIN);
- rtc_tm->tm_hour = readb(S3C2410_RTCHOUR);
- rtc_tm->tm_mday = readb(S3C2410_RTCDATE);
- rtc_tm->tm_mon = readb(S3C2410_RTCMON);
- rtc_tm->tm_year = readb(S3C2410_RTCYEAR);
- rtc_tm->tm_sec = readb(S3C2410_RTCSEC);
-
- /* the only way to work out wether the system was mid-update
- * when we read it is to check the second counter, and if it
- * is zero, then we re-try the entire read
- */
-
- if (rtc_tm->tm_sec == 0 && !have_retried) {
- have_retried = 1;
- goto retry_get_time;
- }
-
- pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
- rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
- rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
-
- BCD_TO_BIN(rtc_tm->tm_sec);
- BCD_TO_BIN(rtc_tm->tm_min);
- BCD_TO_BIN(rtc_tm->tm_hour);
- BCD_TO_BIN(rtc_tm->tm_mday);
- BCD_TO_BIN(rtc_tm->tm_mon);
- BCD_TO_BIN(rtc_tm->tm_year);
-
- rtc_tm->tm_year += 100;
- rtc_tm->tm_mon -= 1;
-
- return 0;
-}
-
-
-static int s3c2410_rtc_settime(struct rtc_time *tm)
-{
- /* the rtc gets round the y2k problem by just not supporting it */
-
- if (tm->tm_year < 100)
- return -EINVAL;
-
- writeb(BIN2BCD(tm->tm_sec), S3C2410_RTCSEC);
- writeb(BIN2BCD(tm->tm_min), S3C2410_RTCMIN);
- writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR);
- writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE);
- writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON);
- writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR);
-
- return 0;
-}
-
-static int s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm)
-{
- struct rtc_time *alm_tm = &alrm->time;
- unsigned int alm_en;
-
- alm_tm->tm_sec = readb(S3C2410_ALMSEC);
- alm_tm->tm_min = readb(S3C2410_ALMMIN);
- alm_tm->tm_hour = readb(S3C2410_ALMHOUR);
- alm_tm->tm_mon = readb(S3C2410_ALMMON);
- alm_tm->tm_mday = readb(S3C2410_ALMDATE);
- alm_tm->tm_year = readb(S3C2410_ALMYEAR);
-
- alm_en = readb(S3C2410_RTCALM);
-
- pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
- alm_en,
- alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
- alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
-
-
- /* decode the alarm enable field */
-
- if (alm_en & S3C2410_RTCALM_SECEN) {
- BCD_TO_BIN(alm_tm->tm_sec);
- } else {
- alm_tm->tm_sec = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_MINEN) {
- BCD_TO_BIN(alm_tm->tm_min);
- } else {
- alm_tm->tm_min = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_HOUREN) {
- BCD_TO_BIN(alm_tm->tm_hour);
- } else {
- alm_tm->tm_hour = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_DAYEN) {
- BCD_TO_BIN(alm_tm->tm_mday);
- } else {
- alm_tm->tm_mday = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_MONEN) {
- BCD_TO_BIN(alm_tm->tm_mon);
- alm_tm->tm_mon -= 1;
- } else {
- alm_tm->tm_mon = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_YEAREN) {
- BCD_TO_BIN(alm_tm->tm_year);
- } else {
- alm_tm->tm_year = 0xffff;
- }
-
- /* todo - set alrm->enabled ? */
-
- return 0;
-}
-
-static int s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm)
-{
- struct rtc_time *tm = &alrm->time;
- unsigned int alrm_en;
-
- pr_debug("s3c2410_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
- alrm->enabled,
- tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
- tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
-
- if (alrm->enabled || 1) {
- alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
- writeb(0x00, S3C2410_RTCALM);
-
- if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
- alrm_en |= S3C2410_RTCALM_SECEN;
- writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC);
- }
-
- if (tm->tm_min < 60 && tm->tm_min >= 0) {
- alrm_en |= S3C2410_RTCALM_MINEN;
- writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN);
- }
-
- if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
- alrm_en |= S3C2410_RTCALM_HOUREN;
- writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR);
- }
-
- pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
-
- writeb(alrm_en, S3C2410_RTCALM);
- enable_irq_wake(s3c2410_rtc_alarmno);
- } else {
- alrm_en = readb(S3C2410_RTCALM);
- alrm_en &= ~S3C2410_RTCALM_ALMEN;
- writeb(alrm_en, S3C2410_RTCALM);
- disable_irq_wake(s3c2410_rtc_alarmno);
- }
-
- return 0;
-}
-
-static int s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case RTC_AIE_OFF:
- case RTC_AIE_ON:
- s3c2410_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
- return 0;
-
- case RTC_PIE_OFF:
- case RTC_PIE_ON:
- s3c2410_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
- return 0;
-
- case RTC_IRQP_READ:
- return put_user(s3c2410_rtc_freq, (unsigned long __user *)arg);
-
- case RTC_IRQP_SET:
- if (arg < 1 || arg > 64)
- return -EINVAL;
-
- if (!capable(CAP_SYS_RESOURCE))
- return -EACCES;
-
- /* check for power of 2 */
-
- if ((arg & (arg-1)) != 0)
- return -EINVAL;
-
- pr_debug("s3c2410_rtc: setting frequency %ld\n", arg);
-
- s3c2410_rtc_setfreq(arg);
- return 0;
-
- case RTC_UIE_ON:
- case RTC_UIE_OFF:
- return -EINVAL;
- }
-
- return -EINVAL;
-}
-
-static int s3c2410_rtc_proc(char *buf)
-{
- unsigned int rtcalm = readb(S3C2410_RTCALM);
- unsigned int ticnt = readb (S3C2410_TICNT);
- char *p = buf;
-
- p += sprintf(p, "alarm_IRQ\t: %s\n",
- (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" );
- p += sprintf(p, "periodic_IRQ\t: %s\n",
- (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
- p += sprintf(p, "periodic_freq\t: %d\n", s3c2410_rtc_freq);
-
- return p - buf;
-}
-
-static int s3c2410_rtc_open(void)
-{
- int ret;
-
- ret = request_irq(s3c2410_rtc_alarmno, s3c2410_rtc_alarmirq,
- IRQF_DISABLED, "s3c2410-rtc alarm", NULL);
-
- if (ret)
- printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_alarmno);
-
- ret = request_irq(s3c2410_rtc_tickno, s3c2410_rtc_tickirq,
- IRQF_DISABLED, "s3c2410-rtc tick", NULL);
-
- if (ret) {
- printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_tickno);
- goto tick_err;
- }
-
- return ret;
-
- tick_err:
- free_irq(s3c2410_rtc_alarmno, NULL);
- return ret;
-}
-
-static void s3c2410_rtc_release(void)
-{
- /* do not clear AIE here, it may be needed for wake */
-
- s3c2410_rtc_setpie(0);
- free_irq(s3c2410_rtc_alarmno, NULL);
- free_irq(s3c2410_rtc_tickno, NULL);
-}
-
-static struct rtc_ops s3c2410_rtcops = {
- .owner = THIS_MODULE,
- .open = s3c2410_rtc_open,
- .release = s3c2410_rtc_release,
- .ioctl = s3c2410_rtc_ioctl,
- .read_time = s3c2410_rtc_gettime,
- .set_time = s3c2410_rtc_settime,
- .read_alarm = s3c2410_rtc_getalarm,
- .set_alarm = s3c2410_rtc_setalarm,
- .proc = s3c2410_rtc_proc,
-};
-
-static void s3c2410_rtc_enable(struct platform_device *pdev, int en)
-{
- unsigned int tmp;
-
- if (s3c2410_rtc_base == NULL)
- return;
-
- if (!en) {
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON);
-
- tmp = readb(S3C2410_TICNT);
- writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT);
- } else {
- /* re-enable the device, and check it is ok */
-
- if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
- dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
-
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON);
- }
-
- if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
- dev_info(&pdev->dev, "removing S3C2410_RTCCON_CNTSEL\n");
-
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON);
- }
-
- if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
- dev_info(&pdev->dev, "removing S3C2410_RTCCON_CLKRST\n");
-
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON);
- }
- }
-}
-
-static int s3c2410_rtc_remove(struct platform_device *dev)
-{
- unregister_rtc(&s3c2410_rtcops);
-
- s3c2410_rtc_setpie(0);
- s3c2410_rtc_setaie(0);
-
- if (s3c2410_rtc_mem != NULL) {
- pr_debug("s3c2410_rtc: releasing s3c2410_rtc_mem\n");
- iounmap(s3c2410_rtc_base);
- release_resource(s3c2410_rtc_mem);
- kfree(s3c2410_rtc_mem);
- }
-
- return 0;
-}
-
-static int s3c2410_rtc_probe(struct platform_device *pdev)
-{
- struct resource *res;
- int ret;
-
- pr_debug("%s: probe=%p\n", __FUNCTION__, pdev);
-
- /* find the IRQs */
-
- s3c2410_rtc_tickno = platform_get_irq(pdev, 1);
- if (s3c2410_rtc_tickno < 0) {
- dev_err(&pdev->dev, "no irq for rtc tick\n");
- return -ENOENT;
- }
-
- s3c2410_rtc_alarmno = platform_get_irq(pdev, 0);
- if (s3c2410_rtc_alarmno < 0) {
- dev_err(&pdev->dev, "no irq for alarm\n");
- return -ENOENT;
- }
-
- pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
- s3c2410_rtc_tickno, s3c2410_rtc_alarmno);
-
- /* get the memory region */
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory region resource\n");
- return -ENOENT;
- }
-
- s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1,
- pdev->name);
-
- if (s3c2410_rtc_mem == NULL) {
- dev_err(&pdev->dev, "failed to reserve memory region\n");
- ret = -ENOENT;
- goto exit_err;
- }
-
- s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1);
- if (s3c2410_rtc_base == NULL) {
- dev_err(&pdev->dev, "failed ioremap()\n");
- ret = -EINVAL;
- goto exit_err;
- }
-
- s3c2410_rtc_mem = res;
- pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base);
-
- pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
-
- /* check to see if everything is setup correctly */
-
- s3c2410_rtc_enable(pdev, 1);
-
- pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
-
- s3c2410_rtc_setfreq(s3c2410_rtc_freq);
-
- /* register RTC and exit */
-
- register_rtc(&s3c2410_rtcops);
- return 0;
-
- exit_err:
- dev_err(&pdev->dev, "error %d during initialisation\n", ret);
-
- return ret;
-}
-
-#ifdef CONFIG_PM
-
-/* S3C2410 RTC Power management control */
-
-static struct timespec s3c2410_rtc_delta;
-
-static int ticnt_save;
-
-static int s3c2410_rtc_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
- /* save TICNT for anyone using periodic interrupts */
-
- ticnt_save = readb(S3C2410_TICNT);
-
- /* calculate time delta for suspend */
-
- s3c2410_rtc_gettime(&tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- save_time_delta(&s3c2410_rtc_delta, &time);
- s3c2410_rtc_enable(pdev, 0);
-
- return 0;
-}
-
-static int s3c2410_rtc_resume(struct platform_device *pdev)
-{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
- s3c2410_rtc_enable(pdev, 1);
- s3c2410_rtc_gettime(&tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- restore_time_delta(&s3c2410_rtc_delta, &time);
-
- writeb(ticnt_save, S3C2410_TICNT);
- return 0;
-}
-#else
-#define s3c2410_rtc_suspend NULL
-#define s3c2410_rtc_resume NULL
-#endif
-
-static struct platform_driver s3c2410_rtcdrv = {
- .probe = s3c2410_rtc_probe,
- .remove = s3c2410_rtc_remove,
- .suspend = s3c2410_rtc_suspend,
- .resume = s3c2410_rtc_resume,
- .driver = {
- .name = "s3c2410-rtc",
- .owner = THIS_MODULE,
- },
-};
-
-static char __initdata banner[] = "S3C2410 RTC, (c) 2004 Simtec Electronics\n";
-
-static int __init s3c2410_rtc_init(void)
-{
- printk(banner);
- return platform_driver_register(&s3c2410_rtcdrv);
-}
-
-static void __exit s3c2410_rtc_exit(void)
-{
- platform_driver_unregister(&s3c2410_rtcdrv);
-}
-
-module_init(s3c2410_rtc_init);
-module_exit(s3c2410_rtc_exit);
-
-MODULE_DESCRIPTION("S3C24XX RTC Driver");
-MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c
index b956c7babd1..99e5272e3c5 100644
--- a/drivers/char/scx200_gpio.c
+++ b/drivers/char/scx200_gpio.c
@@ -44,7 +44,7 @@ struct nsc_gpio_ops scx200_gpio_ops = {
.gpio_change = scx200_gpio_change,
.gpio_current = scx200_gpio_current
};
-EXPORT_SYMBOL(scx200_gpio_ops);
+EXPORT_SYMBOL_GPL(scx200_gpio_ops);
static int scx200_gpio_open(struct inode *inode, struct file *file)
{
@@ -69,7 +69,7 @@ static const struct file_operations scx200_gpio_fileops = {
.release = scx200_gpio_release,
};
-struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
+static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
static int __init scx200_gpio_init(void)
{
diff --git a/drivers/char/selection.c b/drivers/char/selection.c
index 71093a9fc46..74cff839c85 100644
--- a/drivers/char/selection.c
+++ b/drivers/char/selection.c
@@ -33,7 +33,7 @@ extern void poke_blanked_console(void);
/* Variables for selection control. */
/* Use a dynamic buffer, instead of static (Dec 1994) */
-struct vc_data *sel_cons; /* must not be disallocated */
+struct vc_data *sel_cons; /* must not be deallocated */
static volatile int sel_start = -1; /* cleared by clear_selection */
static int sel_end;
static int sel_buffer_lth;
diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c
index a1d303f9a33..c0ef0f0e580 100644
--- a/drivers/char/specialix.c
+++ b/drivers/char/specialix.c
@@ -1087,24 +1087,16 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p
port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
spin_unlock_irqrestore(&bp->lock, flags);
dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
- baud = C_BAUD(tty);
+ baud = tty_get_baud_rate(tty);
- if (baud & CBAUDEX) {
- baud &= ~CBAUDEX;
- if (baud < 1 || baud > 2)
- port->tty->termios->c_cflag &= ~CBAUDEX;
- else
- baud += 15;
- }
- if (baud == 15) {
+ if (baud == 38400) {
if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
baud ++;
if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
baud += 2;
}
-
- if (!baud_table[baud]) {
+ if (!baud) {
/* Drop DTR & exit */
dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
if (!SX_CRTSCTS (tty)) {
@@ -1134,7 +1126,7 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p
"This is an untested option, please be carefull.\n",
port_No (port), tmp);
else
- tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
+ tmp = (((SX_OSCFREQ + baud/2) / baud +
CD186x_TPC/2) / CD186x_TPC);
if ((tmp < 0x10) && time_before(again, jiffies)) {
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index ee3ca8f1768..0ad6cb081db 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -208,7 +208,7 @@ static void send_sig_all(int sig)
struct task_struct *p;
for_each_process(p) {
- if (p->mm && p->pid != 1)
+ if (p->mm && !is_init(p))
/* Not swapper, init nor kernel thread */
force_sig(sig, p);
}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index bb0d9199e99..333741770f1 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -129,6 +129,7 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */
/* Semaphore to protect creating and releasing a tty. This is shared with
vt.c for deeply disgusting hack reasons */
DEFINE_MUTEX(tty_mutex);
+EXPORT_SYMBOL(tty_mutex);
#ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
@@ -160,17 +161,11 @@ static void release_mem(struct tty_struct *tty, int idx);
* been initialized in any way but has been zeroed
*
* Locking: none
- * FIXME: use kzalloc
*/
static struct tty_struct *alloc_tty_struct(void)
{
- struct tty_struct *tty;
-
- tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
- if (tty)
- memset(tty, 0, sizeof(struct tty_struct));
- return tty;
+ return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
}
static void tty_buffer_free_all(struct tty_struct *);
@@ -483,10 +478,9 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
tb->used += space;
copied += space;
chars += space;
- }
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- while (unlikely(size > copied));
+ /* There is a small chance that we need to split the data over
+ several buffers. If this is the case we must loop */
+ } while (unlikely(size > copied));
return copied;
}
EXPORT_SYMBOL(tty_insert_flip_string);
@@ -521,10 +515,9 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
copied += space;
chars += space;
flags += space;
- }
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- while (unlikely(size > copied));
+ /* There is a small chance that we need to split the data over
+ several buffers. If this is the case we must loop */
+ } while (unlikely(size > copied));
return copied;
}
EXPORT_SYMBOL(tty_insert_flip_string_flags);
@@ -626,9 +619,9 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
{
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
tty->termios->c_line = num;
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
}
/*
@@ -1346,9 +1339,9 @@ static void do_tty_hangup(void *data)
*/
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
{
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
*tty->termios = tty->driver->init_termios;
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
}
/* Defer ldisc switch */
@@ -2072,8 +2065,9 @@ fail_no_mem:
/* call the tty release_mem routine to clean out this slot */
release_mem_out:
- printk(KERN_INFO "init_dev: ldisc open failed, "
- "clearing slot %d\n", idx);
+ if (printk_ratelimit())
+ printk(KERN_INFO "init_dev: ldisc open failed, "
+ "clearing slot %d\n", idx);
release_mem(tty, idx);
goto end_init;
}
@@ -2726,6 +2720,8 @@ static int tty_fasync(int fd, struct file * filp, int on)
* Locking:
* Called functions take tty_ldisc_lock
* current->signal->tty check is safe without locks
+ *
+ * FIXME: may race normal receive processing
*/
static int tiocsti(struct tty_struct *tty, char __user *p)
@@ -2748,18 +2744,21 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
* @tty; tty
* @arg: user buffer for result
*
- * Copies the kernel idea of the window size into the user buffer. No
- * locking is done.
+ * Copies the kernel idea of the window size into the user buffer.
*
- * FIXME: Returning random values racing a window size set is wrong
- * should lock here against that
+ * Locking: tty->termios_sem is taken to ensure the winsize data
+ * is consistent.
*/
static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
{
- if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
- return -EFAULT;
- return 0;
+ int err;
+
+ mutex_lock(&tty->termios_mutex);
+ err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
+ mutex_unlock(&tty->termios_mutex);
+
+ return err ? -EFAULT: 0;
}
/**
@@ -2772,12 +2771,11 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
* actually has driver level meaning and triggers a VC resize.
*
* Locking:
- * The console_sem is used to ensure we do not try and resize
- * the console twice at once.
- * FIXME: Two racing size sets may leave the console and kernel
- * parameters disagreeing. Is this exploitable ?
- * FIXME: Random values racing a window size get is wrong
- * should lock here against that
+ * Called function use the console_sem is used to ensure we do
+ * not try and resize the console twice at once.
+ * The tty->termios_sem is used to ensure we don't double
+ * resize and get confused. Lock order - tty->termios.sem before
+ * console sem
*/
static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
@@ -2787,17 +2785,18 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
return -EFAULT;
+
+ mutex_lock(&tty->termios_mutex);
if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
- return 0;
+ goto done;
+
#ifdef CONFIG_VT
if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
- int rc;
-
- acquire_console_sem();
- rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row);
- release_console_sem();
- if (rc)
- return -ENXIO;
+ if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col,
+ tmp_ws.ws_row)) {
+ mutex_unlock(&tty->termios_mutex);
+ return -ENXIO;
+ }
}
#endif
if (tty->pgrp > 0)
@@ -2806,6 +2805,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
kill_pg(real_tty->pgrp, SIGWINCH, 1);
tty->winsize = tmp_ws;
real_tty->winsize = tmp_ws;
+done:
+ mutex_unlock(&tty->termios_mutex);
return 0;
}
@@ -2880,9 +2881,7 @@ static int fionbio(struct file *file, int __user *p)
* Locking:
* Takes tasklist lock internally to walk sessions
* Takes task_lock() when updating signal->tty
- *
- * FIXME: tty_mutex is needed to protect signal->tty references.
- * FIXME: why task_lock on the signal->tty reference ??
+ * Takes tty_mutex() to protect tty instance
*
*/
@@ -2917,9 +2916,11 @@ static int tiocsctty(struct tty_struct *tty, int arg)
} else
return -EPERM;
}
+ mutex_lock(&tty_mutex);
task_lock(current);
current->signal->tty = tty;
task_unlock(current);
+ mutex_unlock(&tty_mutex);
current->signal->tty_old_pgrp = 0;
tty->session = current->signal->session;
tty->pgrp = process_group(current);
@@ -2959,8 +2960,6 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
* permitted where the tty session is our session.
*
* Locking: None
- *
- * FIXME: current->signal->tty referencing is unsafe.
*/
static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
@@ -3039,19 +3038,20 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
* timed break functionality.
*
* Locking:
- * None
+ * atomic_write_lock serializes
*
- * FIXME:
- * What if two overlap
*/
static int send_break(struct tty_struct *tty, unsigned int duration)
{
+ if (mutex_lock_interruptible(&tty->atomic_write_lock))
+ return -EINTR;
tty->driver->break_ctl(tty, -1);
if (!signal_pending(current)) {
msleep_interruptible(duration);
}
tty->driver->break_ctl(tty, 0);
+ mutex_unlock(&tty->atomic_write_lock);
if (signal_pending(current))
return -EINTR;
return 0;
@@ -3144,6 +3144,8 @@ int tty_ioctl(struct inode * inode, struct file * file,
if (tty_paranoia_check(tty, inode, "tty_ioctl"))
return -EINVAL;
+ /* CHECKME: is this safe as one end closes ? */
+
real_tty = tty;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
@@ -3580,7 +3582,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty_buffer_init(tty);
INIT_WORK(&tty->buf.work, flush_to_ldisc, tty);
init_MUTEX(&tty->buf.pty_sem);
- init_MUTEX(&tty->termios_sem);
+ mutex_init(&tty->termios_mutex);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index 4ad47d321bd..3b6fa7b0be8 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -20,6 +20,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/bitops.h>
+#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -131,7 +132,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
/* FIXME: we need to decide on some locking/ordering semantics
for the set_termios notification eventually */
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
*tty->termios = *new_termios;
unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
@@ -176,7 +177,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
(ld->set_termios)(tty, &old_termios);
tty_ldisc_deref(ld);
}
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
}
/**
@@ -284,13 +285,13 @@ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
{
struct sgttyb tmp;
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
tmp.sg_ispeed = 0;
tmp.sg_ospeed = 0;
tmp.sg_erase = tty->termios->c_cc[VERASE];
tmp.sg_kill = tty->termios->c_cc[VKILL];
tmp.sg_flags = get_sgflags(tty);
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
@@ -345,12 +346,12 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
return -EFAULT;
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
termios = *tty->termios;
termios.c_cc[VERASE] = tmp.sg_erase;
termios.c_cc[VKILL] = tmp.sg_kill;
set_sgflags(&termios, tmp.sg_flags);
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
change_termios(tty, &termios);
return 0;
}
@@ -422,24 +423,28 @@ static int set_ltchars(struct tty_struct * tty, struct ltchars __user * ltchars)
*
* Send a high priority character to the tty even if stopped
*
- * Locking: none
- *
- * FIXME: overlapping calls with start/stop tty lose state of tty
+ * Locking: none for xchar method, write ordering for write method.
*/
-static void send_prio_char(struct tty_struct *tty, char ch)
+static int send_prio_char(struct tty_struct *tty, char ch)
{
int was_stopped = tty->stopped;
if (tty->driver->send_xchar) {
tty->driver->send_xchar(tty, ch);
- return;
+ return 0;
}
+
+ if (mutex_lock_interruptible(&tty->atomic_write_lock))
+ return -ERESTARTSYS;
+
if (was_stopped)
start_tty(tty);
tty->driver->write(tty, &ch, 1);
if (was_stopped)
stop_tty(tty);
+ mutex_unlock(&tty->atomic_write_lock);
+ return 0;
}
int n_tty_ioctl(struct tty_struct * tty, struct file * file,
@@ -513,11 +518,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
break;
case TCIOFF:
if (STOP_CHAR(tty) != __DISABLED_CHAR)
- send_prio_char(tty, STOP_CHAR(tty));
+ return send_prio_char(tty, STOP_CHAR(tty));
break;
case TCION:
if (START_CHAR(tty) != __DISABLED_CHAR)
- send_prio_char(tty, START_CHAR(tty));
+ return send_prio_char(tty, START_CHAR(tty));
break;
default:
return -EINVAL;
@@ -592,11 +597,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
case TIOCSSOFTCAR:
if (get_user(arg, (unsigned int __user *) arg))
return -EFAULT;
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
return 0;
default:
return -ENOIOCTLCMD;
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index a9247b5213d..bd7a98c6ea7 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -474,14 +474,15 @@ static const struct file_operations vcs_fops = {
static struct class *vc_class;
-void vcs_make_devfs(struct tty_struct *tty)
+void vcs_make_sysfs(struct tty_struct *tty)
{
class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1),
NULL, "vcs%u", tty->index + 1);
class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129),
NULL, "vcsa%u", tty->index + 1);
}
-void vcs_remove_devfs(struct tty_struct *tty)
+
+void vcs_remove_sysfs(struct tty_struct *tty)
{
class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 1));
class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 129));
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index da7e66a2a38..fb75da940b5 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -63,6 +63,13 @@
*
* Removed console_lock, enabled interrupts across all console operations
* 13 March 2001, Andrew Morton
+ *
+ * Fixed UTF-8 mode so alternate charset modes always work according
+ * to control sequences interpreted in do_con_trol function
+ * preserving backward VT100 semigraphics compatibility,
+ * malformed UTF sequences represented as sequences of replacement glyphs,
+ * original codes or '?' as a last resort if replacement glyph is undefined
+ * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006
*/
#include <linux/module.h>
@@ -128,8 +135,8 @@ const struct consw *conswitchp;
#define DEFAULT_BELL_PITCH 750
#define DEFAULT_BELL_DURATION (HZ/8)
-extern void vcs_make_devfs(struct tty_struct *tty);
-extern void vcs_remove_devfs(struct tty_struct *tty);
+extern void vcs_make_sysfs(struct tty_struct *tty);
+extern void vcs_remove_sysfs(struct tty_struct *tty);
extern void console_map_init(void);
#ifdef CONFIG_PROM_CONSOLE
@@ -730,7 +737,8 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
visual_init(vc, currcons, 1);
if (!*vc->vc_uni_pagedir_loc)
con_set_default_unimap(vc);
- vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
+ if (!vc->vc_kmalloced)
+ vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
if (!vc->vc_screenbuf) {
kfree(vc);
vc_cons[currcons].d = NULL;
@@ -878,8 +886,17 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
return err;
}
+int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
+{
+ int rc;
+
+ acquire_console_sem();
+ rc = vc_resize(vc, cols, lines);
+ release_console_sem();
+ return rc;
+}
-void vc_disallocate(unsigned int currcons)
+void vc_deallocate(unsigned int currcons)
{
WARN_CONSOLE_UNLOCKED();
@@ -2005,17 +2022,23 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
/* Do no translation at all in control states */
if (vc->vc_state != ESnormal) {
tc = c;
- } else if (vc->vc_utf) {
+ } else if (vc->vc_utf && !vc->vc_disp_ctrl) {
/* Combine UTF-8 into Unicode */
- /* Incomplete characters silently ignored */
+ /* Malformed sequences as sequences of replacement glyphs */
+rescan_last_byte:
if(c > 0x7f) {
- if (vc->vc_utf_count > 0 && (c & 0xc0) == 0x80) {
- vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
- vc->vc_utf_count--;
- if (vc->vc_utf_count == 0)
- tc = c = vc->vc_utf_char;
- else continue;
+ if (vc->vc_utf_count) {
+ if ((c & 0xc0) == 0x80) {
+ vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
+ if (--vc->vc_utf_count) {
+ vc->vc_npar++;
+ continue;
+ }
+ tc = c = vc->vc_utf_char;
+ } else
+ goto replacement_glyph;
} else {
+ vc->vc_npar = 0;
if ((c & 0xe0) == 0xc0) {
vc->vc_utf_count = 1;
vc->vc_utf_char = (c & 0x1f);
@@ -2032,14 +2055,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
vc->vc_utf_count = 5;
vc->vc_utf_char = (c & 0x01);
} else
- vc->vc_utf_count = 0;
+ goto replacement_glyph;
continue;
}
} else {
+ if (vc->vc_utf_count)
+ goto replacement_glyph;
tc = c;
- vc->vc_utf_count = 0;
}
- } else { /* no utf */
+ } else { /* no utf or alternate charset mode */
tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c];
}
@@ -2054,31 +2078,33 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
* direct-to-font zone in UTF-8 mode.
*/
ok = tc && (c >= 32 ||
- (!vc->vc_utf && !(((vc->vc_disp_ctrl ? CTRL_ALWAYS
- : CTRL_ACTION) >> c) & 1)))
+ !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 :
+ vc->vc_utf || ((CTRL_ACTION >> c) & 1)))
&& (c != 127 || vc->vc_disp_ctrl)
&& (c != 128+27);
if (vc->vc_state == ESnormal && ok) {
/* Now try to find out how to display it */
tc = conv_uni_to_pc(vc, tc);
- if ( tc == -4 ) {
+ if (tc & ~charmask) {
+ if ( tc == -4 ) {
/* If we got -4 (not found) then see if we have
defined a replacement character (U+FFFD) */
- tc = conv_uni_to_pc(vc, 0xfffd);
-
- /* One reason for the -4 can be that we just
- did a clear_unimap();
- try at least to show something. */
- if (tc == -4)
- tc = c;
- } else if ( tc == -3 ) {
- /* Bad hash table -- hope for the best */
- tc = c;
- }
- if (tc & ~charmask)
- continue; /* Conversion failed */
+replacement_glyph:
+ tc = conv_uni_to_pc(vc, 0xfffd);
+ if (!(tc & ~charmask))
+ goto display_glyph;
+ } else if ( tc != -3 )
+ continue; /* nothing to display */
+ /* no hash table or no replacement --
+ * hope for the best */
+ if ( c & ~charmask )
+ tc = '?';
+ else
+ tc = c;
+ }
+display_glyph:
if (vc->vc_need_wrap || vc->vc_decim)
FLUSH
if (vc->vc_need_wrap) {
@@ -2102,6 +2128,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
vc->vc_x++;
draw_to = (vc->vc_pos += 2);
}
+ if (vc->vc_utf_count) {
+ if (vc->vc_npar) {
+ vc->vc_npar--;
+ goto display_glyph;
+ }
+ vc->vc_utf_count = 0;
+ c = orig;
+ goto rescan_last_byte;
+ }
continue;
}
FLUSH
@@ -2498,7 +2533,7 @@ static int con_open(struct tty_struct *tty, struct file *filp)
tty->winsize.ws_col = vc_cons[currcons].d->vc_cols;
}
release_console_sem();
- vcs_make_devfs(tty);
+ vcs_make_sysfs(tty);
return ret;
}
}
@@ -2511,7 +2546,7 @@ static int con_open(struct tty_struct *tty, struct file *filp)
* and taking a ref against the tty while we're in the process of forgetting
* about it and cleaning things up.
*
- * This is because vcs_remove_devfs() can sleep and will drop the BKL.
+ * This is because vcs_remove_sysfs() can sleep and will drop the BKL.
*/
static void con_close(struct tty_struct *tty, struct file *filp)
{
@@ -2524,7 +2559,7 @@ static void con_close(struct tty_struct *tty, struct file *filp)
vc->vc_tty = NULL;
tty->driver_data = NULL;
release_console_sem();
- vcs_remove_devfs(tty);
+ vcs_remove_sysfs(tty);
mutex_unlock(&tty_mutex);
/*
* tty_mutex is released, but we still hold BKL, so there is
@@ -3765,6 +3800,7 @@ EXPORT_SYMBOL(default_blu);
EXPORT_SYMBOL(update_region);
EXPORT_SYMBOL(redraw_screen);
EXPORT_SYMBOL(vc_resize);
+EXPORT_SYMBOL(vc_lock_resize);
EXPORT_SYMBOL(fg_console);
EXPORT_SYMBOL(console_blank_hook);
EXPORT_SYMBOL(console_blanked);
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c
index a5628a8b662..a53e382cc10 100644
--- a/drivers/char/vt_ioctl.c
+++ b/drivers/char/vt_ioctl.c
@@ -96,7 +96,7 @@ do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_str
if (!perm)
return -EPERM;
if (!i && v == K_NOSUCHMAP) {
- /* disallocate map */
+ /* deallocate map */
key_map = key_maps[s];
if (s && key_map) {
key_maps[s] = NULL;
@@ -819,20 +819,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (arg > MAX_NR_CONSOLES)
return -ENXIO;
if (arg == 0) {
- /* disallocate all unused consoles, but leave 0 */
+ /* deallocate all unused consoles, but leave 0 */
acquire_console_sem();
for (i=1; i<MAX_NR_CONSOLES; i++)
if (! VT_BUSY(i))
- vc_disallocate(i);
+ vc_deallocate(i);
release_console_sem();
} else {
- /* disallocate a single console, if possible */
+ /* deallocate a single console, if possible */
arg--;
if (VT_BUSY(arg))
return -EBUSY;
if (arg) { /* leave 0 */
acquire_console_sem();
- vc_disallocate(arg);
+ vc_deallocate(arg);
release_console_sem();
}
}
@@ -847,11 +847,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (get_user(ll, &vtsizes->v_rows) ||
get_user(cc, &vtsizes->v_cols))
return -EFAULT;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- acquire_console_sem();
- vc_resize(vc_cons[i].d, cc, ll);
- release_console_sem();
- }
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ vc_lock_resize(vc_cons[i].d, cc, ll);
return 0;
}
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index fff89c2d88f..77ab7e020da 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -165,6 +165,13 @@ config EP93XX_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called ep93xx_wdt.
+config OMAP_WATCHDOG
+ tristate "OMAP Watchdog"
+ depends on WATCHDOG && (ARCH_OMAP16XX || ARCH_OMAP24XX)
+ help
+ Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to
+ enable the OMAP1610/OMAP1710 watchdog timer.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
@@ -510,6 +517,14 @@ config SH_WDT
To compile this driver as a module, choose M here: the
module will be called shwdt.
+config SH_WDT_MMAP
+ bool "Allow mmap of SH WDT"
+ default n
+ depends on SH_WDT
+ help
+ If you say Y here, user applications will be able to mmap the
+ WDT/CPG registers.
+#
# SPARC64 Architecture
config WATCHDOG_CP1XXX
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 6ab77b61a64..5099f8be8cc 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture
obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o
+obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c
new file mode 100644
index 00000000000..8f90b90a502
--- /dev/null
+++ b/drivers/char/watchdog/omap_wdt.c
@@ -0,0 +1,391 @@
+/*
+ * linux/drivers/char/watchdog/omap_wdt.c
+ *
+ * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog
+ *
+ * Author: MontaVista Software, Inc.
+ * <gdavis@mvista.com> or <source@mvista.com>
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * History:
+ *
+ * 20030527: George G. Davis <gdavis@mvista.com>
+ * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c
+ * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
+ * Based on SoftDog driver by Alan Cox <alan@redhat.com>
+ *
+ * Copyright (c) 2004 Texas Instruments.
+ * 1. Modified to support OMAP1610 32-KHz watchdog timer
+ * 2. Ported to 2.6 kernel
+ *
+ * Copyright (c) 2005 David Brownell
+ * Use the driver model and standard identifiers; handle bigger timeouts.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/bitops.h>
+
+#include <asm/arch/prcm.h>
+
+#include "omap_wdt.h"
+
+static unsigned timer_margin;
+module_param(timer_margin, uint, 0);
+MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
+
+static int omap_wdt_users;
+static struct clk *armwdt_ck = NULL;
+static struct clk *mpu_wdt_ick = NULL;
+static struct clk *mpu_wdt_fck = NULL;
+
+static unsigned int wdt_trgr_pattern = 0x1234;
+
+static void omap_wdt_ping(void)
+{
+ /* wait for posted write to complete */
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ cpu_relax();
+ wdt_trgr_pattern = ~wdt_trgr_pattern;
+ omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR));
+ /* wait for posted write to complete */
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ cpu_relax();
+ /* reloaded WCRR from WLDR */
+}
+
+static void omap_wdt_enable(void)
+{
+ /* Sequence to enable the watchdog */
+ omap_writel(0xBBBB, OMAP_WATCHDOG_SPR);
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+ cpu_relax();
+ omap_writel(0x4444, OMAP_WATCHDOG_SPR);
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+ cpu_relax();
+}
+
+static void omap_wdt_disable(void)
+{
+ /* sequence required to disable watchdog */
+ omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+ cpu_relax();
+ omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+ cpu_relax();
+}
+
+static void omap_wdt_adjust_timeout(unsigned new_timeout)
+{
+ if (new_timeout < TIMER_MARGIN_MIN)
+ new_timeout = TIMER_MARGIN_DEFAULT;
+ if (new_timeout > TIMER_MARGIN_MAX)
+ new_timeout = TIMER_MARGIN_MAX;
+ timer_margin = new_timeout;
+}
+
+static void omap_wdt_set_timeout(void)
+{
+ u32 pre_margin = GET_WLDR_VAL(timer_margin);
+
+ /* just count up at 32 KHz */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+ cpu_relax();
+ omap_writel(pre_margin, OMAP_WATCHDOG_LDR);
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+ cpu_relax();
+}
+
+/*
+ * Allow only one task to hold it open
+ */
+
+static int omap_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users))
+ return -EBUSY;
+
+ if (cpu_is_omap16xx())
+ clk_enable(armwdt_ck); /* Enable the clock */
+
+ if (cpu_is_omap24xx()) {
+ clk_enable(mpu_wdt_ick); /* Enable the interface clock */
+ clk_enable(mpu_wdt_fck); /* Enable the functional clock */
+ }
+
+ /* initialize prescaler */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
+ cpu_relax();
+ omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL);
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
+ cpu_relax();
+
+ omap_wdt_set_timeout();
+ omap_wdt_enable();
+ return 0;
+}
+
+static int omap_wdt_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer unless NOWAYOUT is defined.
+ */
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ omap_wdt_disable();
+
+ if (cpu_is_omap16xx()) {
+ clk_disable(armwdt_ck); /* Disable the clock */
+ clk_put(armwdt_ck);
+ armwdt_ck = NULL;
+ }
+
+ if (cpu_is_omap24xx()) {
+ clk_disable(mpu_wdt_ick); /* Disable the clock */
+ clk_disable(mpu_wdt_fck); /* Disable the clock */
+ clk_put(mpu_wdt_ick);
+ clk_put(mpu_wdt_fck);
+ mpu_wdt_ick = NULL;
+ mpu_wdt_fck = NULL;
+ }
+#else
+ printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
+#endif
+ omap_wdt_users = 0;
+ return 0;
+}
+
+static ssize_t
+omap_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* Refresh LOAD_TIME. */
+ if (len)
+ omap_wdt_ping();
+ return len;
+}
+
+static int
+omap_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int new_margin;
+ static struct watchdog_info ident = {
+ .identity = "OMAP Watchdog",
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 0,
+ };
+
+ switch (cmd) {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident));
+ case WDIOC_GETSTATUS:
+ return put_user(0, (int __user *)arg);
+ case WDIOC_GETBOOTSTATUS:
+ if (cpu_is_omap16xx())
+ return put_user(omap_readw(ARM_SYSST),
+ (int __user *)arg);
+ if (cpu_is_omap24xx())
+ return put_user(omap_prcm_get_reset_sources(),
+ (int __user *)arg);
+ case WDIOC_KEEPALIVE:
+ omap_wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)arg))
+ return -EFAULT;
+ omap_wdt_adjust_timeout(new_margin);
+
+ omap_wdt_disable();
+ omap_wdt_set_timeout();
+ omap_wdt_enable();
+
+ omap_wdt_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timer_margin, (int __user *)arg);
+ }
+}
+
+static struct file_operations omap_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = omap_wdt_write,
+ .ioctl = omap_wdt_ioctl,
+ .open = omap_wdt_open,
+ .release = omap_wdt_release,
+};
+
+static struct miscdevice omap_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &omap_wdt_fops
+};
+
+static int __init omap_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res, *mem;
+ int ret;
+
+ /* reserve static register mappings */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ mem = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (mem == NULL)
+ return -EBUSY;
+
+ platform_set_drvdata(pdev, mem);
+
+ omap_wdt_users = 0;
+
+ if (cpu_is_omap16xx()) {
+ armwdt_ck = clk_get(&pdev->dev, "armwdt_ck");
+ if (IS_ERR(armwdt_ck)) {
+ ret = PTR_ERR(armwdt_ck);
+ armwdt_ck = NULL;
+ goto fail;
+ }
+ }
+
+ if (cpu_is_omap24xx()) {
+ mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick");
+ if (IS_ERR(mpu_wdt_ick)) {
+ ret = PTR_ERR(mpu_wdt_ick);
+ mpu_wdt_ick = NULL;
+ goto fail;
+ }
+ mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck");
+ if (IS_ERR(mpu_wdt_fck)) {
+ ret = PTR_ERR(mpu_wdt_fck);
+ mpu_wdt_fck = NULL;
+ goto fail;
+ }
+ }
+
+ omap_wdt_disable();
+ omap_wdt_adjust_timeout(timer_margin);
+
+ omap_wdt_miscdev.dev = &pdev->dev;
+ ret = misc_register(&omap_wdt_miscdev);
+ if (ret)
+ goto fail;
+
+ pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin);
+
+ /* autogate OCP interface clock */
+ omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG);
+ return 0;
+
+fail:
+ if (armwdt_ck)
+ clk_put(armwdt_ck);
+ if (mpu_wdt_ick)
+ clk_put(mpu_wdt_ick);
+ if (mpu_wdt_fck)
+ clk_put(mpu_wdt_fck);
+ release_resource(mem);
+ return ret;
+}
+
+static void omap_wdt_shutdown(struct platform_device *pdev)
+{
+ omap_wdt_disable();
+}
+
+static int omap_wdt_remove(struct platform_device *pdev)
+{
+ struct resource *mem = platform_get_drvdata(pdev);
+ misc_deregister(&omap_wdt_miscdev);
+ release_resource(mem);
+ if (armwdt_ck)
+ clk_put(armwdt_ck);
+ if (mpu_wdt_ick)
+ clk_put(mpu_wdt_ick);
+ if (mpu_wdt_fck)
+ clk_put(mpu_wdt_fck);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/* REVISIT ... not clear this is the best way to handle system suspend; and
+ * it's very inappropriate for selective device suspend (e.g. suspending this
+ * through sysfs rather than by stopping the watchdog daemon). Also, this
+ * may not play well enough with NOWAYOUT...
+ */
+
+static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (omap_wdt_users)
+ omap_wdt_disable();
+ return 0;
+}
+
+static int omap_wdt_resume(struct platform_device *pdev)
+{
+ if (omap_wdt_users) {
+ omap_wdt_enable();
+ omap_wdt_ping();
+ }
+ return 0;
+}
+
+#else
+#define omap_wdt_suspend NULL
+#define omap_wdt_resume NULL
+#endif
+
+static struct platform_driver omap_wdt_driver = {
+ .probe = omap_wdt_probe,
+ .remove = omap_wdt_remove,
+ .shutdown = omap_wdt_shutdown,
+ .suspend = omap_wdt_suspend,
+ .resume = omap_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "omap_wdt",
+ },
+};
+
+static int __init omap_wdt_init(void)
+{
+ return platform_driver_register(&omap_wdt_driver);
+}
+
+static void __exit omap_wdt_exit(void)
+{
+ platform_driver_unregister(&omap_wdt_driver);
+}
+
+module_init(omap_wdt_init);
+module_exit(omap_wdt_exit);
+
+MODULE_AUTHOR("George G. Davis");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/char/watchdog/omap_wdt.h b/drivers/char/watchdog/omap_wdt.h
new file mode 100644
index 00000000000..52a532a5114
--- /dev/null
+++ b/drivers/char/watchdog/omap_wdt.h
@@ -0,0 +1,64 @@
+/*
+ * linux/drivers/char/watchdog/omap_wdt.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ * OMAP Watchdog timer register definitions
+ *
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _OMAP_WATCHDOG_H
+#define _OMAP_WATCHDOG_H
+
+#define OMAP1610_WATCHDOG_BASE 0xfffeb000
+#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */
+
+#ifdef CONFIG_ARCH_OMAP24XX
+#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE
+#else
+#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE
+#define RM_RSTST_WKUP 0
+#endif
+
+#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00)
+#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10)
+#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14)
+#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24)
+#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28)
+#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c)
+#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30)
+#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34)
+#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48)
+
+/* Using the prescaler, the OMAP watchdog could go for many
+ * months before firing. These limits work without scaling,
+ * with the 60 second default assumed by most tools and docs.
+ */
+#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */
+#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */
+#define TIMER_MARGIN_MIN 1
+
+#define PTV 0 /* prescale */
+#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1)
+
+#endif /* _OMAP_WATCHDOG_H */
diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c
index 1355038f104..e5b8c64f1d6 100644
--- a/drivers/char/watchdog/shwdt.c
+++ b/drivers/char/watchdog/shwdt.c
@@ -27,7 +27,7 @@
#include <linux/notifier.h>
#include <linux/ioport.h>
#include <linux/fs.h>
-
+#include <linux/mm.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/watchdog.h>
@@ -125,7 +125,6 @@ static void sh_wdt_start(void)
/**
* sh_wdt_stop - Stop the Watchdog
- *
* Stops the watchdog.
*/
static void sh_wdt_stop(void)
@@ -141,22 +140,20 @@ static void sh_wdt_stop(void)
/**
* sh_wdt_keepalive - Keep the Userspace Watchdog Alive
- *
* The Userspace watchdog got a KeepAlive: schedule the next heartbeat.
*/
-static void sh_wdt_keepalive(void)
+static inline void sh_wdt_keepalive(void)
{
next_heartbeat = jiffies + (heartbeat * HZ);
}
/**
* sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat
- *
* Set the Userspace Watchdog heartbeat
*/
static int sh_wdt_set_heartbeat(int t)
{
- if ((t < 1) || (t > 3600)) /* arbitrary upper limit */
+ if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */
return -EINVAL;
heartbeat = t;
@@ -165,7 +162,6 @@ static int sh_wdt_set_heartbeat(int t)
/**
* sh_wdt_ping - Ping the Watchdog
- *
* @data: Unused
*
* Clears overflow bit, resets timer counter.
@@ -182,14 +178,13 @@ static void sh_wdt_ping(unsigned long data)
sh_wdt_write_cnt(0);
mod_timer(&timer, next_ping_period(clock_division_ratio));
- } else {
- printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n");
- }
+ } else
+ printk(KERN_WARNING PFX "Heartbeat lost! Will not ping "
+ "the watchdog\n");
}
/**
* sh_wdt_open - Open the Device
- *
* @inode: inode of device
* @file: file handle of device
*
@@ -209,7 +204,6 @@ static int sh_wdt_open(struct inode *inode, struct file *file)
/**
* sh_wdt_close - Close the Device
- *
* @inode: inode of device
* @file: file handle of device
*
@@ -220,7 +214,8 @@ static int sh_wdt_close(struct inode *inode, struct file *file)
if (shwdt_expect_close == 42) {
sh_wdt_stop();
} else {
- printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
+ printk(KERN_CRIT PFX "Unexpected close, not "
+ "stopping watchdog!\n");
sh_wdt_keepalive();
}
@@ -232,7 +227,6 @@ static int sh_wdt_close(struct inode *inode, struct file *file)
/**
* sh_wdt_write - Write to Device
- *
* @file: file handle of device
* @buf: buffer to write
* @count: length of buffer
@@ -264,8 +258,56 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf,
}
/**
- * sh_wdt_ioctl - Query Device
+ * sh_wdt_mmap - map WDT/CPG registers into userspace
+ * @file: file structure for the device
+ * @vma: VMA to map the registers into
+ *
+ * A simple mmap() implementation for the corner cases where the counter
+ * needs to be mapped in userspace directly. Due to the relatively small
+ * size of the area, neighbouring registers not necessarily tied to the
+ * CPG will also be accessible through the register page, so this remains
+ * configurable for users that really know what they're doing.
*
+ * Additionaly, the register page maps in the CPG register base relative
+ * to the nearest page-aligned boundary, which requires that userspace do
+ * the appropriate CPU subtype math for calculating the page offset for
+ * the counter value.
+ */
+static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int ret = -ENOSYS;
+
+#ifdef CONFIG_SH_WDT_MMAP
+ unsigned long addr;
+
+ /* Only support the simple cases where we map in a register page. */
+ if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff)
+ return -EINVAL;
+
+ /*
+ * Pick WTCNT as the start, it's usually the first register after the
+ * FRQCR, and neither one are generally page-aligned out of the box.
+ */
+ addr = WTCNT & ~(PAGE_SIZE - 1);
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
+ PAGE_SIZE, vma->vm_page_prot)) {
+ printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n",
+ __FUNCTION__);
+ return -EAGAIN;
+ }
+
+ ret = 0;
+#endif
+
+ return ret;
+}
+
+/**
+ * sh_wdt_ioctl - Query Device
* @inode: inode of device
* @file: file handle of device
* @cmd: watchdog command
@@ -326,7 +368,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file,
/**
* sh_wdt_notify_sys - Notifier Handler
- *
* @this: notifier block
* @code: notifier event
* @unused: unused
@@ -337,9 +378,8 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file,
static int sh_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
- if (code == SYS_DOWN || code == SYS_HALT) {
+ if (code == SYS_DOWN || code == SYS_HALT)
sh_wdt_stop();
- }
return NOTIFY_DONE;
}
@@ -351,10 +391,12 @@ static const struct file_operations sh_wdt_fops = {
.ioctl = sh_wdt_ioctl,
.open = sh_wdt_open,
.release = sh_wdt_close,
+ .mmap = sh_wdt_mmap,
};
static struct watchdog_info sh_wdt_info = {
- .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "SH WDT",
};
@@ -371,7 +413,6 @@ static struct miscdevice sh_wdt_miscdev = {
/**
* sh_wdt_init - Initialize module
- *
* Registers the device and notifier handler. Actual device
* initialization is handled by sh_wdt_open().
*/
@@ -381,15 +422,15 @@ static int __init sh_wdt_init(void)
if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) {
clock_division_ratio = WTCSR_CKS_4096;
- printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n",
- clock_division_ratio);
+ printk(KERN_INFO PFX "clock_division_ratio value must "
+ "be 0x5<=x<=0x7, using %d\n", clock_division_ratio);
}
- if (sh_wdt_set_heartbeat(heartbeat))
- {
+ rc = sh_wdt_set_heartbeat(heartbeat);
+ if (unlikely(rc)) {
heartbeat = WATCHDOG_HEARTBEAT;
- printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n",
- heartbeat);
+ printk(KERN_INFO PFX "heartbeat value must "
+ "be 1<=x<=3600, using %d\n", heartbeat);
}
init_timer(&timer);
@@ -397,15 +438,16 @@ static int __init sh_wdt_init(void)
timer.data = 0;
rc = register_reboot_notifier(&sh_wdt_notifier);
- if (rc) {
- printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc);
+ if (unlikely(rc)) {
+ printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n",
+ rc);
return rc;
}
rc = misc_register(&sh_wdt_miscdev);
- if (rc) {
- printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n",
- sh_wdt_miscdev.minor, rc);
+ if (unlikely(rc)) {
+ printk(KERN_ERR PFX "Can't register miscdev on "
+ "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc);
unregister_reboot_notifier(&sh_wdt_notifier);
return rc;
}
@@ -418,7 +460,6 @@ static int __init sh_wdt_init(void)
/**
* sh_wdt_exit - Deinitialize module
- *
* Unregisters the device and notifier handler. Actual device
* deinitialization is handled by sh_wdt_close().
*/
@@ -434,14 +475,13 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(clock_division_ratio, int, 0);
-MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7.");
+MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
module_param(nowayout, int, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
module_init(sh_wdt_init);
module_exit(sh_wdt_exit);
-
diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c
index 6078e2f5881..3a365e159d8 100644
--- a/drivers/eisa/eisa-bus.c
+++ b/drivers/eisa/eisa-bus.c
@@ -128,9 +128,23 @@ static int eisa_bus_match (struct device *dev, struct device_driver *drv)
return 0;
}
+static int eisa_bus_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct eisa_device *edev = to_eisa_device(dev);
+ int i = 0;
+ int length = 0;
+
+ add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
+ "MODALIAS=" EISA_DEVICE_MODALIAS_FMT, edev->id.sig);
+ envp[i] = NULL;
+ return 0;
+}
+
struct bus_type eisa_bus_type = {
.name = "eisa",
.match = eisa_bus_match,
+ .uevent = eisa_bus_uevent,
};
int eisa_driver_register (struct eisa_driver *edrv)
@@ -160,6 +174,14 @@ static ssize_t eisa_show_state (struct device *dev, struct device_attribute *att
static DEVICE_ATTR(enabled, S_IRUGO, eisa_show_state, NULL);
+static ssize_t eisa_show_modalias (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct eisa_device *edev = to_eisa_device (dev);
+ return sprintf (buf, EISA_DEVICE_MODALIAS_FMT "\n", edev->id.sig);
+}
+
+static DEVICE_ATTR(modalias, S_IRUGO, eisa_show_modalias, NULL);
+
static int __init eisa_init_device (struct eisa_root_device *root,
struct eisa_device *edev,
int slot)
@@ -209,6 +231,7 @@ static int __init eisa_register_device (struct eisa_device *edev)
device_create_file (&edev->dev, &dev_attr_signature);
device_create_file (&edev->dev, &dev_attr_enabled);
+ device_create_file (&edev->dev, &dev_attr_modalias);
return 0;
}
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index b9e3886d9e1..b8b596d5778 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -123,6 +123,26 @@ static void __init dmi_save_devices(struct dmi_header *dm)
dev->type = *d++ & 0x7f;
dev->name = dmi_string(dm, *d);
dev->device_data = NULL;
+ list_add(&dev->list, &dmi_devices);
+ }
+}
+
+static void __init dmi_save_oem_strings_devices(struct dmi_header *dm)
+{
+ int i, count = *(u8 *)(dm + 1);
+ struct dmi_device *dev;
+
+ for (i = 1; i <= count; i++) {
+ dev = dmi_alloc(sizeof(*dev));
+ if (!dev) {
+ printk(KERN_ERR
+ "dmi_save_oem_strings_devices: out of memory.\n");
+ break;
+ }
+
+ dev->type = DMI_DEV_TYPE_OEM_STRING;
+ dev->name = dmi_string(dm, i);
+ dev->device_data = NULL;
list_add(&dev->list, &dmi_devices);
}
@@ -181,6 +201,9 @@ static void __init dmi_decode(struct dmi_header *dm)
case 10: /* Onboard Devices Information */
dmi_save_devices(dm);
break;
+ case 11: /* OEM Strings */
+ dmi_save_oem_strings_devices(dm);
+ break;
case 38: /* IPMI Device Information */
dmi_save_ipmi_device(dm);
}
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0e31a0c496e..9b88b25b6ed 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -53,7 +53,7 @@ config SENSORS_ADM1021
config SENSORS_ADM1025
tristate "Analog Devices ADM1025 and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select HWMON_VID
help
If you say yes here you get support for Analog Devices ADM1025
@@ -94,6 +94,16 @@ config SENSORS_ADM9240
This driver can also be built as a module. If so, the module
will be called adm9240.
+config SENSORS_K8TEMP
+ tristate "AMD K8 processor sensor"
+ depends on HWMON && X86 && PCI && EXPERIMENTAL
+ help
+ If you say yes here you get support for the temperature
+ sensor(s) inside your AMD K8 CPU.
+
+ This driver can also be built as a module. If so, the module
+ will be called k8temp.
+
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
depends on HWMON && I2C && EXPERIMENTAL
@@ -121,7 +131,7 @@ config SENSORS_ATXP1
config SENSORS_DS1621
tristate "Dallas Semiconductor DS1621 and DS1625"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for Dallas Semiconductor
DS1621 and DS1625 sensor chips.
@@ -141,7 +151,7 @@ config SENSORS_F71805F
config SENSORS_FSCHER
tristate "FSC Hermes"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for Fujitsu Siemens
Computers Hermes sensor chips.
@@ -151,7 +161,7 @@ config SENSORS_FSCHER
config SENSORS_FSCPOS
tristate "FSC Poseidon"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for Fujitsu Siemens
Computers Poseidon sensor chips.
@@ -171,7 +181,7 @@ config SENSORS_GL518SM
config SENSORS_GL520SM
tristate "Genesys Logic GL520SM"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select HWMON_VID
help
If you say yes here you get support for Genesys Logic GL520SM
@@ -186,15 +196,15 @@ config SENSORS_IT87
select I2C_ISA
select HWMON_VID
help
- If you say yes here you get support for ITE IT87xx sensor chips
- and clones: SiS960.
+ If you say yes here you get support for ITE IT8705F, IT8712F,
+ IT8716F and IT8718F sensor chips, and the SiS960 clone.
This driver can also be built as a module. If so, the module
will be called it87.
config SENSORS_LM63
tristate "National Semiconductor LM63"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for the National Semiconductor
LM63 remote diode digital temperature sensor with integrated fan
@@ -231,7 +241,7 @@ config SENSORS_LM75
config SENSORS_LM77
tristate "National Semiconductor LM77"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for National Semiconductor LM77
sensor chips.
@@ -241,7 +251,7 @@ config SENSORS_LM77
config SENSORS_LM78
tristate "National Semiconductor LM78 and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select I2C_ISA
select HWMON_VID
help
@@ -284,7 +294,7 @@ config SENSORS_LM85
config SENSORS_LM87
tristate "National Semiconductor LM87"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select HWMON_VID
help
If you say yes here you get support for National Semiconductor LM87
@@ -309,7 +319,7 @@ config SENSORS_LM90
config SENSORS_LM92
tristate "National Semiconductor LM92 and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for National Semiconductor LM92
and Maxim MAX6635 sensor chips.
@@ -319,7 +329,7 @@ config SENSORS_LM92
config SENSORS_MAX1619
tristate "Maxim MAX1619 sensor chip"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for MAX1619 sensor chip.
@@ -354,7 +364,7 @@ config SENSORS_SIS5595
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select I2C_ISA
help
If you say yes here you get support for the integrated fan
@@ -407,8 +417,19 @@ config SENSORS_VIA686A
This driver can also be built as a module. If so, the module
will be called via686a.
+config SENSORS_VT1211
+ tristate "VIA VT1211"
+ depends on HWMON && EXPERIMENTAL
+ select HWMON_VID
+ help
+ If you say yes here then you get support for hardware monitoring
+ features of the VIA VT1211 Super-I/O chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called vt1211.
+
config SENSORS_VT8231
- tristate "VT8231"
+ tristate "VIA VT8231"
depends on HWMON && I2C && PCI && EXPERIMENTAL
select HWMON_VID
select I2C_ISA
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 31415843a91..af01cc64f7d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
+obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
obj-$(CONFIG_SENSORS_LM70) += lm70.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
@@ -45,6 +46,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
+obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
index 35ad1b03272..e5cb0fdab9b 100644
--- a/drivers/hwmon/abituguru.c
+++ b/drivers/hwmon/abituguru.c
@@ -1354,13 +1354,39 @@ LEAVE_UPDATE:
return NULL;
}
+#ifdef CONFIG_PM
+static int abituguru_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct abituguru_data *data = platform_get_drvdata(pdev);
+ /* make sure all communications with the uguru are done and no new
+ ones are started */
+ mutex_lock(&data->update_lock);
+ return 0;
+}
+
+static int abituguru_resume(struct platform_device *pdev)
+{
+ struct abituguru_data *data = platform_get_drvdata(pdev);
+ /* See if the uGuru is still ready */
+ if (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT)
+ data->uguru_ready = 0;
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+#else
+#define abituguru_suspend NULL
+#define abituguru_resume NULL
+#endif /* CONFIG_PM */
+
static struct platform_driver abituguru_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ABIT_UGURU_NAME,
},
- .probe = abituguru_probe,
- .remove = __devexit_p(abituguru_remove),
+ .probe = abituguru_probe,
+ .remove = __devexit_p(abituguru_remove),
+ .suspend = abituguru_suspend,
+ .resume = abituguru_resume,
};
static int __init abituguru_detect(void)
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index 2b6e74dd4a8..c466329b2ef 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -190,6 +190,21 @@ static int adm1021_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1021_detect);
}
+static struct attribute *adm1021_attributes[] = {
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group adm1021_group = {
+ .attrs = adm1021_attributes,
+};
+
static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
@@ -287,22 +302,19 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
adm1021_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1021_group)))
+ goto error2;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto error2;
+ goto error3;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+error3:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1021_group);
error2:
i2c_detach_client(new_client);
error1:
@@ -326,6 +338,7 @@ static int adm1021_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1021_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c
index a4c859c9fbf..8c562885b54 100644
--- a/drivers/hwmon/adm1025.c
+++ b/drivers/hwmon/adm1025.c
@@ -315,6 +315,49 @@ static int adm1025_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1025_detect);
}
+static struct attribute *adm1025_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group adm1025_group = {
+ .attrs = adm1025_attributes,
+};
+
+static struct attribute *adm1025_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ NULL
+};
+
+static const struct attribute_group adm1025_group_opt = {
+ .attrs = adm1025_attributes_opt,
+};
+
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
@@ -415,46 +458,31 @@ static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind)
adm1025_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1025_group)))
goto exit_detach;
- }
-
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_vrm);
/* Pin 11 is either in4 (+12V) or VID4 */
if (!(config & 0x20)) {
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto exit_remove;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
}
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1025_group);
+ sysfs_remove_group(&new_client->dev.kobj, &adm1025_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -511,6 +539,8 @@ static int adm1025_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1025_group);
+ sysfs_remove_group(&client->dev.kobj, &adm1025_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index 6d4f8b8d358..b4618b2705f 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -323,15 +323,6 @@ static int adm1026_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1026_detect);
}
-static int adm1026_detach_client(struct i2c_client *client)
-{
- struct adm1026_data *data = i2c_get_clientdata(client);
- hwmon_device_unregister(data->class_dev);
- i2c_detach_client(client);
- kfree(data);
- return 0;
-}
-
static int adm1026_read_value(struct i2c_client *client, u8 reg)
{
int res;
@@ -1450,6 +1441,135 @@ static DEVICE_ATTR(temp1_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
+static struct attribute *adm1026_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in12_max.dev_attr.attr,
+ &sensor_dev_attr_in12_min.dev_attr.attr,
+ &sensor_dev_attr_in13_input.dev_attr.attr,
+ &sensor_dev_attr_in13_max.dev_attr.attr,
+ &sensor_dev_attr_in13_min.dev_attr.attr,
+ &sensor_dev_attr_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in14_max.dev_attr.attr,
+ &sensor_dev_attr_in14_min.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ &sensor_dev_attr_in15_max.dev_attr.attr,
+ &sensor_dev_attr_in15_min.dev_attr.attr,
+ &sensor_dev_attr_in16_input.dev_attr.attr,
+ &sensor_dev_attr_in16_max.dev_attr.attr,
+ &sensor_dev_attr_in16_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_div.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_div.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_div.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_div.dev_attr.attr,
+ &sensor_dev_attr_fan8_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &dev_attr_temp1_crit_enable.attr,
+ &dev_attr_temp2_crit_enable.attr,
+ &dev_attr_temp3_crit_enable.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_alarm_mask.attr,
+ &dev_attr_gpio.attr,
+ &dev_attr_gpio_mask.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm3.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm2_enable.attr,
+ &dev_attr_pwm3_enable.attr,
+ &dev_attr_temp1_auto_point1_pwm.attr,
+ &dev_attr_temp2_auto_point1_pwm.attr,
+ &dev_attr_temp3_auto_point1_pwm.attr,
+ &dev_attr_temp1_auto_point2_pwm.attr,
+ &dev_attr_temp2_auto_point2_pwm.attr,
+ &dev_attr_temp3_auto_point2_pwm.attr,
+ &dev_attr_analog_out.attr,
+ NULL
+};
+
+static const struct attribute_group adm1026_group = {
+ .attrs = adm1026_attributes,
+};
+
static int adm1026_detect(struct i2c_adapter *adapter, int address,
int kind)
{
@@ -1554,145 +1674,20 @@ static int adm1026_detect(struct i2c_adapter *adapter, int address,
adm1026_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1026_group)))
+ goto exitdetach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exitdetach;
+ goto exitremove;
}
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in9_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in9_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in9_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in10_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in10_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in10_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in11_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in11_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in11_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in12_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in12_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in12_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in13_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in13_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in13_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in14_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in14_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in14_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in15_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in15_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in15_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in16_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in16_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in16_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan4_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan4_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan4_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan5_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan5_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan5_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan6_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan6_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan6_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan7_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan7_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan7_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan8_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan8_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan8_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_offset.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_offset.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_auto_point1_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_auto_point1_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_auto_point1_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_auto_point2_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_auto_point2_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_auto_point2_temp.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_crit.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_crit.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_enable);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit_enable);
- device_create_file(&new_client->dev, &dev_attr_temp3_crit_enable);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_vrm);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_alarm_mask);
- device_create_file(&new_client->dev, &dev_attr_gpio);
- device_create_file(&new_client->dev, &dev_attr_gpio_mask);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_pwm3);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm3_enable);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_point1_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_point1_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_point1_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_point2_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_point2_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_point2_pwm);
- device_create_file(&new_client->dev, &dev_attr_analog_out);
return 0;
/* Error out and cleanup code */
+exitremove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1026_group);
exitdetach:
i2c_detach_client(new_client);
exitfree:
@@ -1700,6 +1695,17 @@ exitfree:
exit:
return err;
}
+
+static int adm1026_detach_client(struct i2c_client *client)
+{
+ struct adm1026_data *data = i2c_get_clientdata(client);
+ hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1026_group);
+ i2c_detach_client(client);
+ kfree(data);
+ return 0;
+}
+
static int __init sm_adm1026_init(void)
{
return i2c_add_driver(&adm1026_driver);
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index 3bf2da621ae..122683fc91d 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -730,6 +730,61 @@ static int adm1031_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1031_detect);
}
+static struct attribute *adm1031_attributes[] = {
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_auto_fan1_channel.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_crit.attr,
+
+ &dev_attr_auto_temp1_off.attr,
+ &dev_attr_auto_temp1_min.attr,
+ &dev_attr_auto_temp1_max.attr,
+
+ &dev_attr_auto_temp2_off.attr,
+ &dev_attr_auto_temp2_min.attr,
+ &dev_attr_auto_temp2_max.attr,
+
+ &dev_attr_auto_fan1_min_pwm.attr,
+
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group adm1031_group = {
+ .attrs = adm1031_attributes,
+};
+
+static struct attribute *adm1031_attributes_opt[] = {
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_auto_fan2_channel.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_min.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_crit.attr,
+ &dev_attr_auto_temp3_off.attr,
+ &dev_attr_auto_temp3_min.attr,
+ &dev_attr_auto_temp3_max.attr,
+ &dev_attr_auto_fan2_min_pwm.attr,
+ NULL
+};
+
+static const struct attribute_group adm1031_group_opt = {
+ .attrs = adm1031_attributes_opt,
+};
+
/* This function is called by i2c_probe */
static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -789,57 +844,26 @@ static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
adm1031_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1031_group)))
goto exit_detach;
- }
-
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit);
-
- device_create_file(&new_client->dev, &dev_attr_auto_temp1_off);
- device_create_file(&new_client->dev, &dev_attr_auto_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_auto_temp1_max);
-
- device_create_file(&new_client->dev, &dev_attr_auto_temp2_off);
- device_create_file(&new_client->dev, &dev_attr_auto_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_auto_temp2_max);
-
- device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm);
-
- device_create_file(&new_client->dev, &dev_attr_alarms);
if (kind == adm1031) {
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev,
- &dev_attr_auto_fan2_channel);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_crit);
- device_create_file(&new_client->dev, &dev_attr_auto_temp3_off);
- device_create_file(&new_client->dev, &dev_attr_auto_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_auto_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm);
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &adm1031_group_opt)))
+ goto exit_remove;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
}
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1031_group);
+ sysfs_remove_group(&new_client->dev.kobj, &adm1031_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -854,6 +878,8 @@ static int adm1031_detach_client(struct i2c_client *client)
int ret;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1031_group);
+ sysfs_remove_group(&client->dev.kobj, &adm1031_group_opt);
if ((ret = i2c_detach_client(client)) != 0) {
return ret;
}
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 43f6991b588..377961c4a41 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -465,6 +465,45 @@ static ssize_t chassis_clear(struct device *dev,
}
static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear);
+static struct attribute *adm9240_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &dev_attr_temp1_input.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_aout_output.attr,
+ &dev_attr_chassis_clear.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group adm9240_group = {
+ .attrs = adm9240_attributes,
+};
+
/*** sensor chip detect and driver install ***/
@@ -548,72 +587,19 @@ static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind)
adm9240_init_client(new_client);
/* populate sysfs filesystem */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm9240_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan2_div.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_aout_output);
- device_create_file(&new_client->dev, &dev_attr_chassis_clear);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
-
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm9240_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -635,6 +621,7 @@ static int adm9240_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm9240_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index facc1ccb833..57b1c7b7ac3 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -298,12 +298,6 @@ sysfs_in(4);
sysfs_in(5);
sysfs_in(6);
-#define device_create_file_in(client, offset) do { \
- device_create_file(&client->dev, &dev_attr_in##offset##_input); \
- device_create_file(&client->dev, &dev_attr_in##offset##_min); \
- device_create_file(&client->dev, &dev_attr_in##offset##_max); \
-} while (0)
-
/* 3 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
@@ -421,12 +415,6 @@ sysfs_fan(1);
sysfs_fan(2);
sysfs_fan(3);
-#define device_create_file_fan(client, offset) do { \
- device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_min); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
-} while (0)
-
/* 4 Temp. Sensors */
static int sprintf_temp_from_reg(u16 reg, char *buf, int nr)
{
@@ -515,12 +503,6 @@ sysfs_temp(3);
sysfs_temp(4);
/* VID */
-#define device_create_file_temp(client, num) do { \
- device_create_file(&client->dev, &dev_attr_temp##num##_input); \
- device_create_file(&client->dev, &dev_attr_temp##num##_max); \
- device_create_file(&client->dev, &dev_attr_temp##num##_max_hyst); \
-} while (0)
-
static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct asb100_data *data = asb100_update_device(dev);
@@ -528,8 +510,6 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid)
/* VRM */
static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
@@ -549,8 +529,6 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const
/* Alarms */
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm);
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -559,8 +537,6 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
-#define device_create_file_alarms(client) \
-device_create_file(&client->dev, &dev_attr_alarms)
/* 1 PWM */
static ssize_t show_pwm1(struct device *dev, struct device_attribute *attr, char *buf)
@@ -607,10 +583,65 @@ static ssize_t set_pwm_enable1(struct device *dev, struct device_attribute *attr
static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm1, set_pwm1);
static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
show_pwm_enable1, set_pwm_enable1);
-#define device_create_file_pwm1(client) do { \
- device_create_file(&new_client->dev, &dev_attr_pwm1); \
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable); \
-} while (0)
+
+static struct attribute *asb100_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan3_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_max_hyst.attr,
+ &dev_attr_temp4_input.attr,
+ &dev_attr_temp4_max.attr,
+ &dev_attr_temp4_max_hyst.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+
+ NULL
+};
+
+static const struct attribute_group asb100_group = {
+ .attrs = asb100_attributes,
+};
/* This function is called when:
asb100_driver is inserted (when this module is loaded), for each
@@ -810,38 +841,19 @@ static int asb100_detect(struct i2c_adapter *adapter, int address, int kind)
data->fan_min[2] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(2));
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &asb100_group)))
+ goto ERROR3;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto ERROR3;
+ goto ERROR4;
}
- device_create_file_in(new_client, 0);
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- device_create_file_in(new_client, 4);
- device_create_file_in(new_client, 5);
- device_create_file_in(new_client, 6);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- device_create_file_temp(new_client, 3);
- device_create_file_temp(new_client, 4);
-
- device_create_file_vid(new_client);
- device_create_file_vrm(new_client);
-
- device_create_file_alarms(new_client);
-
- device_create_file_pwm1(new_client);
-
return 0;
+ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &asb100_group);
ERROR3:
i2c_detach_client(data->lm75[1]);
i2c_detach_client(data->lm75[0]);
@@ -861,8 +873,10 @@ static int asb100_detach_client(struct i2c_client *client)
int err;
/* main client */
- if (data)
+ if (data) {
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &asb100_group);
+ }
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index 728a1e8b919..0ccdd0750c4 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -27,6 +27,7 @@
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
@@ -116,8 +117,7 @@ static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *att
{
struct atxp1_data *data;
struct i2c_client *client;
- char vid;
- char cvid;
+ int vid, cvid;
unsigned int vcore;
client = to_i2c_client(dev);
@@ -251,6 +251,17 @@ static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *att
*/
static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
+static struct attribute *atxp1_attributes[] = {
+ &dev_attr_gpio1.attr,
+ &dev_attr_gpio2.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group atxp1_group = {
+ .attrs = atxp1_attributes,
+};
+
static int atxp1_attach_adapter(struct i2c_adapter *adapter)
{
@@ -320,21 +331,23 @@ static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_free;
}
+ /* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_gpio1);
- device_create_file(&new_client->dev, &dev_attr_gpio2);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
-
dev_info(&new_client->dev, "Using VRM: %d.%d\n",
data->vrm / 10, data->vrm % 10);
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -349,6 +362,7 @@ static int atxp1_detach_client(struct i2c_client * client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &atxp1_group);
err = i2c_detach_client(client);
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index 478eb4bb857..c849c0c6ee9 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -29,6 +29,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include "lm75.h"
/* Addresses to scan */
@@ -178,6 +179,18 @@ static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
+static struct attribute *ds1621_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group ds1621_group = {
+ .attrs = ds1621_attributes,
+};
+
static int ds1621_attach_adapter(struct i2c_adapter *adapter)
{
@@ -253,21 +266,19 @@ static int ds1621_detect(struct i2c_adapter *adapter, int address,
ds1621_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &ds1621_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
-
return 0;
-/* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
+ exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &ds1621_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -282,6 +293,7 @@ static int ds1621_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &ds1621_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index fd72440faf7..de17a72149d 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -1,7 +1,7 @@
/*
* f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated
* hardware monitoring features
- * Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org>
*
* The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates
* complete hardware monitoring features: voltage, fan and temperature
@@ -31,6 +31,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
static struct platform_device *pdev;
@@ -147,7 +148,7 @@ struct f71805f_data {
u8 temp_high[3];
u8 temp_hyst[3];
u8 temp_mode;
- u8 alarms[3];
+ unsigned long alarms;
};
static inline long in_from_reg(u8 reg)
@@ -311,10 +312,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev)
data->temp[nr] = f71805f_read8(data,
F71805F_REG_TEMP(nr));
}
- for (nr = 0; nr < 3; nr++) {
- data->alarms[nr] = f71805f_read8(data,
- F71805F_REG_STATUS(nr));
- }
+ data->alarms = f71805f_read8(data, F71805F_REG_STATUS(0))
+ + (f71805f_read8(data, F71805F_REG_STATUS(1)) << 8)
+ + (f71805f_read8(data, F71805F_REG_STATUS(2)) << 16);
data->last_updated = jiffies;
data->valid = 1;
@@ -557,8 +557,7 @@ static ssize_t show_alarms_in(struct device *dev, struct device_attribute
{
struct f71805f_data *data = f71805f_update_device(dev);
- return sprintf(buf, "%d\n", data->alarms[0] |
- ((data->alarms[1] & 0x01) << 8));
+ return sprintf(buf, "%lu\n", data->alarms & 0x1ff);
}
static ssize_t show_alarms_fan(struct device *dev, struct device_attribute
@@ -566,7 +565,7 @@ static ssize_t show_alarms_fan(struct device *dev, struct device_attribute
{
struct f71805f_data *data = f71805f_update_device(dev);
- return sprintf(buf, "%d\n", data->alarms[2] & 0x07);
+ return sprintf(buf, "%lu\n", (data->alarms >> 16) & 0x07);
}
static ssize_t show_alarms_temp(struct device *dev, struct device_attribute
@@ -574,7 +573,17 @@ static ssize_t show_alarms_temp(struct device *dev, struct device_attribute
{
struct f71805f_data *data = f71805f_update_device(dev);
- return sprintf(buf, "%d\n", (data->alarms[1] >> 3) & 0x07);
+ return sprintf(buf, "%lu\n", (data->alarms >> 11) & 0x07);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%lu\n", (data->alarms >> bitnr) & 1);
}
static ssize_t show_name(struct device *dev, struct device_attribute
@@ -585,88 +594,189 @@ static ssize_t show_name(struct device *dev, struct device_attribute
return sprintf(buf, "%s\n", data->name);
}
-static struct device_attribute f71805f_dev_attr[] = {
- __ATTR(in0_input, S_IRUGO, show_in0, NULL),
- __ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max),
- __ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min),
- __ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL),
- __ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL),
- __ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL),
- __ATTR(name, S_IRUGO, show_name, NULL),
+static DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL);
+static DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max);
+static DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 1);
+static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 2);
+static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 3);
+static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 4);
+static SENSOR_DEVICE_ATTR(in4_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in5_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 5);
+static SENSOR_DEVICE_ATTR(in5_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in6_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 6);
+static SENSOR_DEVICE_ATTR(in6_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in7_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 7);
+static SENSOR_DEVICE_ATTR(in7_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 8);
+static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 8);
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 2);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 16);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 17);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 18);
+static DEVICE_ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL);
+static DEVICE_ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL);
+static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL);
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *f71805f_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in0_min.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+ &dev_attr_alarms_in.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &dev_attr_alarms_temp.attr,
+ &dev_attr_alarms_fan.attr,
+
+ &dev_attr_name.attr,
+ NULL
};
-static struct sensor_device_attribute f71805f_sensor_attr[] = {
- SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
- SENSOR_ATTR(in1_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 1),
- SENSOR_ATTR(in1_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 1),
- SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
- SENSOR_ATTR(in2_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 2),
- SENSOR_ATTR(in2_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 2),
- SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
- SENSOR_ATTR(in3_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 3),
- SENSOR_ATTR(in3_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 3),
- SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
- SENSOR_ATTR(in4_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 4),
- SENSOR_ATTR(in4_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 4),
- SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
- SENSOR_ATTR(in5_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 5),
- SENSOR_ATTR(in5_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 5),
- SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
- SENSOR_ATTR(in6_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 6),
- SENSOR_ATTR(in6_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 6),
- SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
- SENSOR_ATTR(in7_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 7),
- SENSOR_ATTR(in7_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 7),
- SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
- SENSOR_ATTR(in8_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 8),
- SENSOR_ATTR(in8_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 8),
-
- SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0),
- SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR,
- show_temp_max, set_temp_max, 0),
- SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst, 0),
- SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0),
- SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1),
- SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR,
- show_temp_max, set_temp_max, 1),
- SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst, 1),
- SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1),
- SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2),
- SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR,
- show_temp_max, set_temp_max, 2),
- SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst, 2),
- SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2),
+static const struct attribute_group f71805f_group = {
+ .attrs = f71805f_attributes,
};
-static struct sensor_device_attribute f71805f_fan_attr[] = {
- SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
- SENSOR_ATTR(fan1_min, S_IRUGO | S_IWUSR,
- show_fan_min, set_fan_min, 0),
- SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
- SENSOR_ATTR(fan2_min, S_IRUGO | S_IWUSR,
- show_fan_min, set_fan_min, 1),
- SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
- SENSOR_ATTR(fan3_min, S_IRUGO | S_IWUSR,
- show_fan_min, set_fan_min, 2),
+static struct attribute *f71805f_attributes_fan[3][4] = {
+ {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group f71805f_group_fan[3] = {
+ { .attrs = f71805f_attributes_fan[0] },
+ { .attrs = f71805f_attributes_fan[1] },
+ { .attrs = f71805f_attributes_fan[2] },
};
/*
@@ -714,43 +824,35 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
- data->class_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
- goto exit_free;
- }
-
/* Initialize the F71805F chip */
f71805f_init_device(data);
/* Register sysfs interface files */
- for (i = 0; i < ARRAY_SIZE(f71805f_dev_attr); i++) {
- err = device_create_file(&pdev->dev, &f71805f_dev_attr[i]);
- if (err)
- goto exit_class;
- }
- for (i = 0; i < ARRAY_SIZE(f71805f_sensor_attr); i++) {
- err = device_create_file(&pdev->dev,
- &f71805f_sensor_attr[i].dev_attr);
- if (err)
- goto exit_class;
- }
- for (i = 0; i < ARRAY_SIZE(f71805f_fan_attr); i++) {
- if (!(data->fan_enabled & (1 << (i / 2))))
+ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group)))
+ goto exit_free;
+ for (i = 0; i < 3; i++) {
+ if (!(data->fan_enabled & (1 << i)))
continue;
- err = device_create_file(&pdev->dev,
- &f71805f_fan_attr[i].dev_attr);
- if (err)
- goto exit_class;
+ if ((err = sysfs_create_group(&pdev->dev.kobj,
+ &f71805f_group_fan[i])))
+ goto exit_remove_files;
+ }
+
+ data->class_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit_remove_files;
}
return 0;
-exit_class:
- dev_err(&pdev->dev, "Sysfs interface creation failed\n");
- hwmon_device_unregister(data->class_dev);
+exit_remove_files:
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
+ for (i = 0; i < 3; i++)
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]);
exit_free:
+ platform_set_drvdata(pdev, NULL);
kfree(data);
exit:
return err;
@@ -759,9 +861,13 @@ exit:
static int __devexit f71805f_remove(struct platform_device *pdev)
{
struct f71805f_data *data = platform_get_drvdata(pdev);
+ int i;
platform_set_drvdata(pdev, NULL);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
+ for (i = 0; i < 3; i++)
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]);
kfree(data);
return 0;
diff --git a/drivers/hwmon/fscher.c b/drivers/hwmon/fscher.c
index 6bc76b40763..19717752cfc 100644
--- a/drivers/hwmon/fscher.c
+++ b/drivers/hwmon/fscher.c
@@ -34,6 +34,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -240,47 +241,45 @@ sysfs_alarms(FSCHER_REG_EVENTS)
sysfs_control(FSCHER_REG_CONTROL)
sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET)
-#define device_create_file_fan(client, offset) \
-do { \
- device_create_file(&client->dev, &dev_attr_fan##offset##_status); \
- device_create_file(&client->dev, &dev_attr_pwm##offset); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
-} while (0)
-
-#define device_create_file_temp(client, offset) \
-do { \
- device_create_file(&client->dev, &dev_attr_temp##offset##_status); \
- device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
-} while (0)
-
-#define device_create_file_in(client, offset) \
-do { \
- device_create_file(&client->dev, &dev_attr_in##offset##_input); \
-} while (0)
-
-#define device_create_file_revision(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_revision); \
-} while (0)
-
-#define device_create_file_alarms(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_alarms); \
-} while (0)
-
-#define device_create_file_control(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_control); \
-} while (0)
-
-#define device_create_file_watchdog(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_watchdog_status); \
- device_create_file(&client->dev, &dev_attr_watchdog_control); \
- device_create_file(&client->dev, &dev_attr_watchdog_preset); \
-} while (0)
-
+static struct attribute *fscher_attributes[] = {
+ &dev_attr_revision.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_control.attr,
+
+ &dev_attr_watchdog_status.attr,
+ &dev_attr_watchdog_control.attr,
+ &dev_attr_watchdog_preset.attr,
+
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+
+ &dev_attr_fan1_status.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_fan2_status.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_fan3_status.attr,
+ &dev_attr_fan3_div.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_pwm3.attr,
+
+ &dev_attr_temp1_status.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_status.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_status.attr,
+ &dev_attr_temp3_input.attr,
+ NULL
+};
+
+static const struct attribute_group fscher_group = {
+ .attrs = fscher_attributes,
+};
+
/*
* Real code
*/
@@ -342,31 +341,19 @@ static int fscher_detect(struct i2c_adapter *adapter, int address, int kind)
fscher_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &fscher_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file_revision(new_client);
- device_create_file_alarms(new_client);
- device_create_file_control(new_client);
- device_create_file_watchdog(new_client);
-
- device_create_file_in(new_client, 0);
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- device_create_file_temp(new_client, 3);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &fscher_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -381,6 +368,7 @@ static int fscher_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &fscher_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/fscpos.c b/drivers/hwmon/fscpos.c
index 6dc4846b9ee..ea506a77f9c 100644
--- a/drivers/hwmon/fscpos.c
+++ b/drivers/hwmon/fscpos.c
@@ -38,6 +38,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -432,6 +433,44 @@ static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL);
+static struct attribute *fscpos_attributes[] = {
+ &dev_attr_event.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+
+ &dev_attr_wdog_control.attr,
+ &dev_attr_wdog_preset.attr,
+ &dev_attr_wdog_state.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_status.attr,
+ &dev_attr_temp1_reset.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_status.attr,
+ &dev_attr_temp2_reset.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_status.attr,
+ &dev_attr_temp3_reset.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_status.attr,
+ &dev_attr_fan1_ripple.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_status.attr,
+ &dev_attr_fan2_ripple.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_status.attr,
+ &dev_attr_fan3_ripple.attr,
+ NULL
+};
+
+static const struct attribute_group fscpos_group = {
+ .attrs = fscpos_attributes,
+};
+
static int fscpos_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
@@ -497,42 +536,19 @@ static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind)
dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &fscpos_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_event);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_wdog_control);
- device_create_file(&new_client->dev, &dev_attr_wdog_preset);
- device_create_file(&new_client->dev, &dev_attr_wdog_state);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_status);
- device_create_file(&new_client->dev, &dev_attr_temp1_reset);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_status);
- device_create_file(&new_client->dev, &dev_attr_temp2_reset);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_status);
- device_create_file(&new_client->dev, &dev_attr_temp3_reset);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_status);
- device_create_file(&new_client->dev, &dev_attr_fan1_ripple);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_status);
- device_create_file(&new_client->dev, &dev_attr_fan2_ripple);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_fan3_input);
- device_create_file(&new_client->dev, &dev_attr_fan3_status);
- device_create_file(&new_client->dev, &dev_attr_fan3_ripple);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &fscpos_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -547,6 +563,7 @@ static int fscpos_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &fscpos_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index 6606aabdb49..c103640455a 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -44,6 +44,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
@@ -340,6 +341,42 @@ static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO,
static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO,
show_beep_mask, set_beep_mask);
+static struct attribute *gl518_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+
+ &dev_attr_fan1_auto.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+ NULL
+};
+
+static const struct attribute_group gl518_group = {
+ .attrs = gl518_attributes,
+};
+
/*
* Real code
*/
@@ -420,43 +457,19 @@ static int gl518_detect(struct i2c_adapter *adapter, int address, int kind)
gl518_init_client((struct i2c_client *) new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &gl518_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_fan1_auto);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_beep_enable);
- device_create_file(&new_client->dev, &dev_attr_beep_mask);
-
return 0;
-/* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
-
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &gl518_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -490,6 +503,7 @@ static int gl518_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &gl518_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index 14e810f3c2c..ebe7b9aaa91 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -30,6 +30,7 @@
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/* Type of the extra sensor */
static unsigned short extra_sensor_type;
@@ -190,55 +191,29 @@ static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL);
#define sysfs_vid(n) \
sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT)
-#define device_create_file_vid(client, n) \
-device_create_file(&client->dev, &dev_attr_cpu##n##_vid)
-
#define sysfs_in(n) \
sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \
sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \
sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \
-#define device_create_file_in(client, n) \
-({device_create_file(&client->dev, &dev_attr_in##n##_input); \
-device_create_file(&client->dev, &dev_attr_in##n##_min); \
-device_create_file(&client->dev, &dev_attr_in##n##_max);})
-
#define sysfs_fan(n) \
sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \
sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \
sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV)
-#define device_create_file_fan(client, n) \
-({device_create_file(&client->dev, &dev_attr_fan##n##_input); \
-device_create_file(&client->dev, &dev_attr_fan##n##_min); \
-device_create_file(&client->dev, &dev_attr_fan##n##_div);})
-
#define sysfs_fan_off(n) \
sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \
-#define device_create_file_fan_off(client, n) \
-device_create_file(&client->dev, &dev_attr_fan##n##_off)
-
#define sysfs_temp(n) \
sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \
sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \
sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST)
-#define device_create_file_temp(client, n) \
-({device_create_file(&client->dev, &dev_attr_temp##n##_input); \
-device_create_file(&client->dev, &dev_attr_temp##n##_max); \
-device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);})
-
#define sysfs_alarms() \
sysfs_ro(alarms, , GL520_REG_ALARMS) \
sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \
sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK)
-#define device_create_file_alarms(client) \
-({device_create_file(&client->dev, &dev_attr_alarms); \
-device_create_file(&client->dev, &dev_attr_beep_enable); \
-device_create_file(&client->dev, &dev_attr_beep_mask);})
-
sysfs_vid(0)
@@ -511,6 +486,59 @@ static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data,
return count;
}
+static struct attribute *gl520_attributes[] = {
+ &dev_attr_cpu0_vid.attr,
+
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_off.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+ NULL
+};
+
+static const struct attribute_group gl520_group = {
+ .attrs = gl520_attributes,
+};
+
+static struct attribute *gl520_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ NULL
+};
+
+static const struct attribute_group gl520_group_opt = {
+ .attrs = gl520_attributes_opt,
+};
+
/*
* Real code
@@ -572,33 +600,39 @@ static int gl520_detect(struct i2c_adapter *adapter, int address, int kind)
gl520_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &gl520_group)))
goto exit_detach;
- }
-
- device_create_file_vid(new_client, 0);
- device_create_file_in(new_client, 0);
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- if (!data->two_temps)
- device_create_file_in(new_client, 4);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan_off(new_client, 1);
+ if (data->two_temps) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_temp2_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp2_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp2_max_hyst)))
+ goto exit_remove_files;
+ } else {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto exit_remove_files;
+ }
- device_create_file_temp(new_client, 1);
- if (data->two_temps)
- device_create_file_temp(new_client, 2);
- device_create_file_alarms(new_client);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
+ }
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &gl520_group);
+ sysfs_remove_group(&new_client->dev.kobj, &gl520_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -652,6 +686,8 @@ static int gl520_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &gl520_group);
+ sysfs_remove_group(&client->dev.kobj, &gl520_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c
index 42b632889dd..8e7b5607f5a 100644
--- a/drivers/hwmon/hdaps.c
+++ b/drivers/hwmon/hdaps.c
@@ -587,7 +587,9 @@ static int __init hdaps_init(void)
input_set_abs_params(hdaps_idev, ABS_Y,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
- input_register_device(hdaps_idev);
+ ret = input_register_device(hdaps_idev);
+ if (ret)
+ goto out_idev;
/* start up our timer for the input device */
init_timer(&hdaps_timer);
@@ -598,6 +600,8 @@ static int __init hdaps_init(void)
printk(KERN_INFO "hdaps: driver successfully loaded.\n");
return 0;
+out_idev:
+ input_free_device(hdaps_idev);
out_group:
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
out_device:
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 06df92b3ee4..323ef06719c 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -4,10 +4,12 @@
Supports: IT8705F Super I/O chip w/LPC interface
IT8712F Super I/O chip w/LPC interface & SMBus
+ IT8716F Super I/O chip w/LPC interface
+ IT8718F Super I/O chip w/LPC interface
Sis950 A clone of the IT8705F
Copyright (C) 2001 Chris Gauthron <chrisg@0-in.com>
- Largely inspired by lm78.c of the same package
+ Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,13 +26,6 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/*
- djg@pdp8.net David Gesswein 7/18/01
- Modified to fix bug with not all alarms enabled.
- Added ability to read battery voltage and select temperature sensor
- type at module load time.
-*/
-
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -42,6 +37,7 @@
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
@@ -50,12 +46,13 @@ static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
static unsigned short isa_address;
/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(it87, it8712);
+I2C_CLIENT_INSMOD_4(it87, it8712, it8716, it8718);
#define REG 0x2e /* The register to read/write */
#define DEV 0x07 /* Register: Logical device select */
#define VAL 0x2f /* The value to read/write */
#define PME 0x04 /* The device with the fan registers in it */
+#define GPIO 0x07 /* The device with the IT8718F VID value in it */
#define DEVID 0x20 /* Register: Device ID */
#define DEVREV 0x22 /* Register: Device Revision */
@@ -77,10 +74,10 @@ static int superio_inw(int reg)
}
static inline void
-superio_select(void)
+superio_select(int ldn)
{
outb(DEV, REG);
- outb(PME, VAL);
+ outb(ldn, VAL);
}
static inline void
@@ -99,20 +96,27 @@ superio_exit(void)
outb(0x02, VAL);
}
+/* Logical device 4 registers */
#define IT8712F_DEVID 0x8712
#define IT8705F_DEVID 0x8705
+#define IT8716F_DEVID 0x8716
+#define IT8718F_DEVID 0x8718
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
+/* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */
+#define IT87_SIO_VID_REG 0xfc /* VID value */
+
/* Update battery voltage after every reading if true */
static int update_vbat;
/* Not all BIOSes properly configure the PWM registers */
static int fix_pwm_polarity;
-/* Chip Type */
-
+/* Values read from Super-I/O config space */
static u16 chip_type;
+static u8 vid_value;
/* Many IT87 constants specified below */
@@ -131,13 +135,21 @@ static u16 chip_type;
#define IT87_REG_ALARM2 0x02
#define IT87_REG_ALARM3 0x03
+/* The IT8718F has the VID value in a different register, in Super-I/O
+ configuration space. */
#define IT87_REG_VID 0x0a
+/* Warning: register 0x0b is used for something completely different in
+ new chips/revisions. I suspect only 16-bit tachometer mode will work
+ for these. */
#define IT87_REG_FAN_DIV 0x0b
+#define IT87_REG_FAN_16BIT 0x0c
/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */
#define IT87_REG_FAN(nr) (0x0d + (nr))
#define IT87_REG_FAN_MIN(nr) (0x10 + (nr))
+#define IT87_REG_FANX(nr) (0x18 + (nr))
+#define IT87_REG_FANX_MIN(nr) (0x1b + (nr))
#define IT87_REG_FAN_MAIN_CTRL 0x13
#define IT87_REG_FAN_CTL 0x14
#define IT87_REG_PWM(nr) (0x15 + (nr))
@@ -169,7 +181,16 @@ static inline u8 FAN_TO_REG(long rpm, int div)
254);
}
+static inline u16 FAN16_TO_REG(long rpm)
+{
+ if (rpm == 0)
+ return 0xffff;
+ return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe);
+}
+
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+/* The divider is fixed to 2 in 16-bit mode */
+#define FAN16_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:1350000/((val)*2))
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\
((val)+500)/1000),-128,127))
@@ -181,7 +202,7 @@ static inline u8 FAN_TO_REG(long rpm, int div)
static int DIV_TO_REG(int val)
{
int answer = 0;
- while ((val >>= 1) != 0)
+ while (answer < 7 && (val >>= 1))
answer++;
return answer;
}
@@ -203,10 +224,11 @@ struct it87_data {
unsigned long last_updated; /* In jiffies */
u8 in[9]; /* Register value */
- u8 in_max[9]; /* Register value */
- u8 in_min[9]; /* Register value */
- u8 fan[3]; /* Register value */
- u8 fan_min[3]; /* Register value */
+ u8 in_max[8]; /* Register value */
+ u8 in_min[8]; /* Register value */
+ u8 has_fan; /* Bitfield, fans enabled */
+ u16 fan[3]; /* Register values, possibly combined */
+ u16 fan_min[3]; /* Register values, possibly combined */
u8 temp[3]; /* Register value */
u8 temp_high[3]; /* Register value */
u8 temp_low[3]; /* Register value */
@@ -243,6 +265,7 @@ static struct i2c_driver it87_driver = {
static struct i2c_driver it87_isa_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "it87-isa",
},
.attach_adapter = it87_isa_attach_adapter,
@@ -544,15 +567,15 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
- int i, min[3];
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+ int min;
u8 old;
mutex_lock(&data->update_lock);
old = it87_read_value(client, IT87_REG_FAN_DIV);
- for (i = 0; i < 3; i++)
- min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i]));
+ /* Save fan min limit */
+ min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
switch (nr) {
case 0:
@@ -572,10 +595,10 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
val |= 0x1 << 6;
it87_write_value(client, IT87_REG_FAN_DIV, val);
- for (i = 0; i < 3; i++) {
- data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i]));
- it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]);
- }
+ /* Restore fan min limit */
+ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]);
+
mutex_unlock(&data->update_lock);
return count;
}
@@ -656,6 +679,59 @@ show_pwm_offset(1);
show_pwm_offset(2);
show_pwm_offset(3);
+/* A different set of callbacks for 16-bit fans */
+static ssize_t show_fan16(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct it87_data *data = it87_update_device(dev);
+ return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan[nr]));
+}
+
+static ssize_t show_fan16_min(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct it87_data *data = it87_update_device(dev);
+ return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan_min[nr]));
+}
+
+static ssize_t set_fan16_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct it87_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ data->fan_min[nr] = FAN16_TO_REG(val);
+ it87_write_value(client, IT87_REG_FAN_MIN(nr),
+ data->fan_min[nr] & 0xff);
+ it87_write_value(client, IT87_REG_FANX_MIN(nr),
+ data->fan_min[nr] >> 8);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* We want to use the same sysfs file names as 8-bit fans, but we need
+ different variable names, so we have to use SENSOR_ATTR instead of
+ SENSOR_DEVICE_ATTR. */
+#define show_fan16_offset(offset) \
+static struct sensor_device_attribute sensor_dev_attr_fan##offset##_input16 \
+ = SENSOR_ATTR(fan##offset##_input, S_IRUGO, \
+ show_fan16, NULL, offset - 1); \
+static struct sensor_device_attribute sensor_dev_attr_fan##offset##_min16 \
+ = SENSOR_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
+ show_fan16_min, set_fan16_min, offset - 1)
+
+show_fan16_offset(1);
+show_fan16_offset(2);
+show_fan16_offset(3);
+
/* Alarms */
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -683,8 +759,6 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm)
static ssize_t
show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -693,8 +767,88 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid)
+
+static struct attribute *it87_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group = {
+ .attrs = it87_attributes,
+};
+
+static struct attribute *it87_attributes_opt[] = {
+ &sensor_dev_attr_fan1_input16.dev_attr.attr,
+ &sensor_dev_attr_fan1_min16.dev_attr.attr,
+ &sensor_dev_attr_fan2_input16.dev_attr.attr,
+ &sensor_dev_attr_fan2_min16.dev_attr.attr,
+ &sensor_dev_attr_fan3_input16.dev_attr.attr,
+ &sensor_dev_attr_fan3_min16.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+
+ &dev_attr_vrm.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group_opt = {
+ .attrs = it87_attributes_opt,
+};
/* This function is called when:
* it87_driver is inserted (when this module is loaded), for each
@@ -720,10 +874,12 @@ static int __init it87_find(unsigned short *address)
superio_enter();
chip_type = superio_inw(DEVID);
if (chip_type != IT8712F_DEVID
+ && chip_type != IT8716F_DEVID
+ && chip_type != IT8718F_DEVID
&& chip_type != IT8705F_DEVID)
goto exit;
- superio_select();
+ superio_select(PME);
if (!(superio_inb(IT87_ACT_REG) & 0x01)) {
pr_info("it87: Device not activated, skipping\n");
goto exit;
@@ -739,6 +895,21 @@ static int __init it87_find(unsigned short *address)
pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
chip_type, *address, superio_inb(DEVREV) & 0x0f);
+ /* Read GPIO config and VID value from LDN 7 (GPIO) */
+ if (chip_type != IT8705F_DEVID) {
+ int reg;
+
+ superio_select(GPIO);
+ if (chip_type == it8718)
+ vid_value = superio_inb(IT87_SIO_VID_REG);
+
+ reg = superio_inb(IT87_SIO_PINX2_REG);
+ if (reg & (1 << 0))
+ pr_info("it87: in3 is VCC (+5V)\n");
+ if (reg & (1 << 1))
+ pr_info("it87: in7 is VCCH (+5V Stand-By)\n");
+ }
+
exit:
superio_exit();
return err;
@@ -799,8 +970,19 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
i = it87_read_value(new_client, IT87_REG_CHIPID);
if (i == 0x90) {
kind = it87;
- if ((is_isa) && (chip_type == IT8712F_DEVID))
- kind = it8712;
+ if (is_isa) {
+ switch (chip_type) {
+ case IT8712F_DEVID:
+ kind = it8712;
+ break;
+ case IT8716F_DEVID:
+ kind = it8716;
+ break;
+ case IT8718F_DEVID:
+ kind = it8718;
+ break;
+ }
+ }
}
else {
if (kind == 0)
@@ -817,6 +999,10 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
name = "it87";
} else if (kind == it8712) {
name = "it8712";
+ } else if (kind == it8716) {
+ name = "it8716";
+ } else if (kind == it8718) {
+ name = "it8718";
}
/* Fill in the remaining client fields and put it into the global list */
@@ -841,76 +1027,103 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
it87_init_client(new_client, data);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &it87_group)))
goto ERROR3;
+
+ /* Do not create fan files for disabled fans */
+ if (data->type == it8716 || data->type == it8718) {
+ /* 16-bit tachometers */
+ if (data->has_fan & (1 << 0)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_input16.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_min16.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 1)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_input16.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_min16.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 2)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_input16.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_min16.dev_attr)))
+ goto ERROR4;
+ }
+ } else {
+ /* 8-bit tachometers with clock divider */
+ if (data->has_fan & (1 << 0)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_input.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_min.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_div.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 1)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_input.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_min.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_div.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 2)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_input.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_min.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_div.dev_attr)))
+ goto ERROR4;
+ }
}
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_type.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_type.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_type.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_alarms);
if (enable_pwm_interface) {
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm1_enable.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm2_enable.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm3_enable.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm1.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm2.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm3.dev_attr);
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm1_enable.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm2_enable.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm3_enable.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm1.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm2.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm3.dev_attr)))
+ goto ERROR4;
}
- if (data->type == it8712) {
+ if (data->type == it8712 || data->type == it8716
+ || data->type == it8718) {
data->vrm = vid_which_vrm();
- device_create_file_vrm(new_client);
- device_create_file_vid(new_client);
+ /* VID reading from Super-I/O config space if available */
+ data->vid = vid_value;
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_vrm))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_cpu0_vid)))
+ goto ERROR4;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR4;
}
return 0;
+ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &it87_group);
+ sysfs_remove_group(&new_client->dev.kobj, &it87_group_opt);
ERROR3:
i2c_detach_client(new_client);
ERROR2:
@@ -928,6 +1141,8 @@ static int it87_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &it87_group);
+ sysfs_remove_group(&client->dev.kobj, &it87_group_opt);
if ((err = i2c_detach_client(client)))
return err;
@@ -1044,6 +1259,22 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data)
data->manual_pwm_ctl[i] = 0xff;
}
+ /* Some chips seem to have default value 0xff for all limit
+ * registers. For low voltage limits it makes no sense and triggers
+ * alarms, so change to 0 instead. For high temperature limits, it
+ * means -1 degree C, which surprisingly doesn't trigger an alarm,
+ * but is still confusing, so change to 127 degrees C. */
+ for (i = 0; i < 8; i++) {
+ tmp = it87_read_value(client, IT87_REG_VIN_MIN(i));
+ if (tmp == 0xff)
+ it87_write_value(client, IT87_REG_VIN_MIN(i), 0);
+ }
+ for (i = 0; i < 3; i++) {
+ tmp = it87_read_value(client, IT87_REG_TEMP_HIGH(i));
+ if (tmp == 0xff)
+ it87_write_value(client, IT87_REG_TEMP_HIGH(i), 127);
+ }
+
/* Check if temperature channnels are reset manually or by some reason */
tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE);
if ((tmp & 0x3f) == 0) {
@@ -1067,6 +1298,18 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data)
data->fan_main_ctrl |= 0x70;
it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
}
+ data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
+
+ /* Set tachometers to 16-bit mode if needed */
+ if (data->type == it8716 || data->type == it8718) {
+ tmp = it87_read_value(client, IT87_REG_FAN_16BIT);
+ if (~tmp & 0x07 & data->has_fan) {
+ dev_dbg(&client->dev,
+ "Setting fan1-3 to 16-bit mode\n");
+ it87_write_value(client, IT87_REG_FAN_16BIT,
+ tmp | 0x07);
+ }
+ }
/* Set current fan mode registers and the default settings for the
* other mode registers */
@@ -1117,18 +1360,26 @@ static struct it87_data *it87_update_device(struct device *dev)
data->in_max[i] =
it87_read_value(client, IT87_REG_VIN_MAX(i));
}
+ /* in8 (battery) has no limit registers */
data->in[8] =
it87_read_value(client, IT87_REG_VIN(8));
- /* Temperature sensor doesn't have limit registers, set
- to min and max value */
- data->in_min[8] = 0;
- data->in_max[8] = 255;
for (i = 0; i < 3; i++) {
- data->fan[i] =
- it87_read_value(client, IT87_REG_FAN(i));
+ /* Skip disabled fans */
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
data->fan_min[i] =
it87_read_value(client, IT87_REG_FAN_MIN(i));
+ data->fan[i] = it87_read_value(client,
+ IT87_REG_FAN(i));
+ /* Add high byte if in 16-bit mode */
+ if (data->type == it8716 || data->type == it8718) {
+ data->fan[i] |= it87_read_value(client,
+ IT87_REG_FANX(i)) << 8;
+ data->fan_min[i] |= it87_read_value(client,
+ IT87_REG_FANX_MIN(i)) << 8;
+ }
}
for (i = 0; i < 3; i++) {
data->temp[i] =
@@ -1139,10 +1390,14 @@ static struct it87_data *it87_update_device(struct device *dev)
it87_read_value(client, IT87_REG_TEMP_LOW(i));
}
- i = it87_read_value(client, IT87_REG_FAN_DIV);
- data->fan_div[0] = i & 0x07;
- data->fan_div[1] = (i >> 3) & 0x07;
- data->fan_div[2] = (i & 0x40) ? 3 : 1;
+ /* Newer chips don't have clock dividers */
+ if ((data->has_fan & 0x07) && data->type != it8716
+ && data->type != it8718) {
+ i = it87_read_value(client, IT87_REG_FAN_DIV);
+ data->fan_div[0] = i & 0x07;
+ data->fan_div[1] = (i >> 3) & 0x07;
+ data->fan_div[2] = (i & 0x40) ? 3 : 1;
+ }
data->alarms =
it87_read_value(client, IT87_REG_ALARM1) |
@@ -1152,9 +1407,11 @@ static struct it87_data *it87_update_device(struct device *dev)
data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE);
/* The 8705 does not have VID capability */
- if (data->type == it8712) {
+ if (data->type == it8712 || data->type == it8716) {
data->vid = it87_read_value(client, IT87_REG_VID);
- data->vid &= 0x1f;
+ /* The older IT8712F revisions had only 5 VID pins,
+ but we assume it is always safe to read 6 bits. */
+ data->vid &= 0x3f;
}
data->last_updated = jiffies;
data->valid = 1;
@@ -1192,8 +1449,9 @@ static void __exit sm_it87_exit(void)
}
-MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>");
-MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver");
+MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>, "
+ "Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F, SiS950 driver");
module_param(update_vbat, bool, 0);
MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
module_param(fix_pwm_polarity, bool, 0);
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c
new file mode 100644
index 00000000000..f58b64ed09e
--- /dev/null
+++ b/drivers/hwmon/k8temp.c
@@ -0,0 +1,294 @@
+/*
+ * k8temp.c - Linux kernel module for hardware monitoring
+ *
+ * Copyright (C) 2006 Rudolf Marek <r.marek@sh.cvut.cz>
+ *
+ * Inspired from the w83785 and amd756 drivers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+#define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000)
+#define REG_TEMP 0xe4
+#define SEL_PLACE 0x40
+#define SEL_CORE 0x04
+
+struct k8temp_data {
+ struct class_device *class_dev;
+ struct mutex update_lock;
+ const char *name;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */
+ u32 temp[2][2]; /* core, place */
+};
+
+static struct k8temp_data *k8temp_update_device(struct device *dev)
+{
+ struct k8temp_data *data = dev_get_drvdata(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u8 tmp;
+
+ mutex_lock(&data->update_lock);
+
+ if (!data->valid
+ || time_after(jiffies, data->last_updated + HZ)) {
+ pci_read_config_byte(pdev, REG_TEMP, &tmp);
+ tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]);
+
+ if (data->sensorsp & SEL_PLACE) {
+ tmp |= SEL_PLACE; /* Select sensor 1, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP,
+ &data->temp[0][1]);
+ }
+
+ if (data->sensorsp & SEL_CORE) {
+ tmp &= ~SEL_PLACE; /* Select sensor 0, core1 */
+ tmp |= SEL_CORE;
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP,
+ &data->temp[1][0]);
+
+ if (data->sensorsp & SEL_PLACE) {
+ tmp |= SEL_PLACE; /* Select sensor 1, core1 */
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP,
+ &data->temp[1][1]);
+ }
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t show_name(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct k8temp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr =
+ to_sensor_dev_attr_2(devattr);
+ int core = attr->nr;
+ int place = attr->index;
+ struct k8temp_data *data = k8temp_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ TEMP_FROM_REG(data->temp[core][place]));
+}
+
+/* core, place */
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 1, 1);
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct pci_device_id k8temp_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
+ { 0 },
+};
+
+MODULE_DEVICE_TABLE(pci, k8temp_ids);
+
+static int __devinit k8temp_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int err;
+ u8 scfg;
+ u32 temp;
+ struct k8temp_data *data;
+ u32 cpuid = cpuid_eax(1);
+
+ /* this feature should be available since SH-C0 core */
+ if ((cpuid == 0xf40) || (cpuid == 0xf50) || (cpuid == 0xf51)) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pci_read_config_byte(pdev, REG_TEMP, &scfg);
+ scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_byte(pdev, REG_TEMP, &scfg);
+
+ if (scfg & (SEL_PLACE | SEL_CORE)) {
+ dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n");
+ err = -ENODEV;
+ goto exit_free;
+ }
+
+ scfg |= (SEL_PLACE | SEL_CORE);
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+
+ /* now we know if we can change core and/or sensor */
+ pci_read_config_byte(pdev, REG_TEMP, &data->sensorsp);
+
+ if (data->sensorsp & SEL_PLACE) {
+ scfg &= ~SEL_CORE; /* Select sensor 1, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_dword(pdev, REG_TEMP, &temp);
+ scfg |= SEL_CORE; /* prepare for next selection */
+ if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */
+ data->sensorsp &= ~SEL_PLACE;
+ }
+
+ if (data->sensorsp & SEL_CORE) {
+ scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_dword(pdev, REG_TEMP, &temp);
+ if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */
+ data->sensorsp &= ~SEL_CORE;
+ }
+
+ data->name = "k8temp";
+ mutex_init(&data->update_lock);
+ dev_set_drvdata(&pdev->dev, data);
+
+ /* Register sysfs hooks */
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ /* sensor can be changed and reports something */
+ if (data->sensorsp & SEL_PLACE) {
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp2_input.dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ /* core can be changed and reports something */
+ if (data->sensorsp & SEL_CORE) {
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp3_input.dev_attr);
+ if (err)
+ goto exit_remove;
+ if (data->sensorsp & SEL_PLACE)
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp4_input.
+ dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
+ goto exit_remove;
+
+ data->class_dev = hwmon_device_register(&pdev->dev);
+
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp2_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp3_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp4_input.dev_attr);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+exit_free:
+ dev_set_drvdata(&pdev->dev, NULL);
+ kfree(data);
+exit:
+ return err;
+}
+
+static void __devexit k8temp_remove(struct pci_dev *pdev)
+{
+ struct k8temp_data *data = dev_get_drvdata(&pdev->dev);
+
+ hwmon_device_unregister(data->class_dev);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp2_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp3_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp4_input.dev_attr);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ dev_set_drvdata(&pdev->dev, NULL);
+ kfree(data);
+}
+
+static struct pci_driver k8temp_driver = {
+ .name = "k8temp",
+ .id_table = k8temp_ids,
+ .probe = k8temp_probe,
+ .remove = __devexit_p(k8temp_remove),
+};
+
+static int __init k8temp_init(void)
+{
+ return pci_register_driver(&k8temp_driver);
+}
+
+static void __exit k8temp_exit(void)
+{
+ pci_unregister_driver(&k8temp_driver);
+}
+
+MODULE_AUTHOR("Rudolf Marek <r.marek@sh.cvut.cz>");
+MODULE_DESCRIPTION("AMD K8 core temperature monitor");
+MODULE_LICENSE("GPL");
+
+module_init(k8temp_init)
+module_exit(k8temp_exit)
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 071f0fc6ade..d69f3cf0712 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -1,7 +1,7 @@
/*
* lm63.c - driver for the National Semiconductor LM63 temperature sensor
* with integrated fan control
- * Copyright (C) 2004-2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2004-2006 Jean Delvare <khali@linux-fr.org>
* Based on the lm90 driver.
*
* The LM63 is a sensor chip made by National Semiconductor. It measures
@@ -46,6 +46,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -330,6 +331,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
return sprintf(buf, "%u\n", data->alarms);
}
+static ssize_t show_alarm(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
set_fan, 1);
@@ -350,8 +361,52 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp8, NULL, 2);
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
set_temp2_crit_hyst);
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+/* Raw alarm file for compatibility */
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *lm63_attributes[] = {
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &dev_attr_temp2_crit_hyst.attr,
+
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group = {
+ .attrs = lm63_attributes,
+};
+
+static struct attribute *lm63_attributes_fan1[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group_fan1 = {
+ .attrs = lm63_attributes_fan1,
+};
+
/*
* Real code
*/
@@ -438,37 +493,26 @@ static int lm63_detect(struct i2c_adapter *adapter, int address, int kind)
lm63_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &lm63_group)))
goto exit_detach;
+ if (data->config & 0x04) { /* tachometer enabled */
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &lm63_group_fan1)))
+ goto exit_remove_files;
}
- if (data->config & 0x04) { /* tachometer enabled */
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_min.dev_attr);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &lm63_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -518,6 +562,8 @@ static int lm63_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm63_group);
+ sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index fc25b90ec24..7c65b8bb6d7 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -112,6 +112,18 @@ static int lm75_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm75_detect);
}
+static struct attribute *lm75_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm75_group = {
+ .attrs = lm75_attributes,
+};
+
/* This function is called by i2c_probe */
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -199,18 +211,19 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
lm75_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm75_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
-
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm75_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -223,6 +236,7 @@ static int lm75_detach_client(struct i2c_client *client)
{
struct lm75_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm75_group);
i2c_detach_client(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index 459cc977380..dd969f1e841 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -212,6 +212,23 @@ static int lm77_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm77_detect);
}
+static struct attribute *lm77_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_crit_hyst.attr,
+ &dev_attr_temp1_min_hyst.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm77_group = {
+ .attrs = lm77_attributes,
+};
+
/* This function is called by i2c_probe */
static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -317,22 +334,19 @@ static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
lm77_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm77_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm77_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -345,6 +359,7 @@ static int lm77_detach_client(struct i2c_client *client)
{
struct lm77_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm77_group);
i2c_detach_client(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index a6ce7abf860..ac1b746df6d 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -175,6 +175,7 @@ static struct i2c_driver lm78_driver = {
static struct i2c_driver lm78_isa_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "lm78-isa",
},
.attach_adapter = lm78_isa_attach_adapter,
@@ -481,6 +482,50 @@ static int lm78_isa_attach_adapter(struct i2c_adapter *adapter)
return lm78_detect(adapter, isa_address, -1);
}
+static struct attribute *lm78_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan3_div.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_cpu0_vid.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm78_group = {
+ .attrs = lm78_attributes,
+};
+
/* This function is called by i2c_probe */
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -615,50 +660,19 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group)))
+ goto ERROR3;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto ERROR3;
+ goto ERROR4;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
- device_create_file(&new_client->dev, &dev_attr_in6_input);
- device_create_file(&new_client->dev, &dev_attr_in6_min);
- device_create_file(&new_client->dev, &dev_attr_in6_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_fan3_input);
- device_create_file(&new_client->dev, &dev_attr_fan3_min);
- device_create_file(&new_client->dev, &dev_attr_fan3_div);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
-
return 0;
+ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
ERROR3:
i2c_detach_client(new_client);
ERROR2:
@@ -676,6 +690,7 @@ static int lm78_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm78_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index b4ccdfc0120..064516d824a 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -394,6 +394,48 @@ static int lm80_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm80_detect);
}
+static struct attribute *lm80_attributes[] = {
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_max.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp1_crit_hyst.attr,
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm80_group = {
+ .attrs = lm80_attributes,
+};
+
static int lm80_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, cur;
@@ -452,48 +494,19 @@ static int lm80_detect(struct i2c_adapter *adapter, int address, int kind)
data->fan_min[1] = lm80_read_value(new_client, LM80_REG_FAN_MIN(2));
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm80_group)))
+ goto error_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto error_detach;
+ goto error_remove;
}
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in6_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
- device_create_file(&new_client->dev, &dev_attr_in6_max);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in6_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+error_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm80_group);
error_detach:
i2c_detach_client(new_client);
error_free:
@@ -508,7 +521,7 @@ static int lm80_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
-
+ sysfs_remove_group(&client->dev.kobj, &lm80_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index 2137d7879df..feb87b41e98 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -1,7 +1,7 @@
/*
* lm83.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org>
*
* Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
* a sensor chip made by National Semiconductor. It reports up to four
@@ -40,6 +40,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -191,6 +192,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
return sprintf(buf, "%d\n", data->alarms);
}
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm83_data *data = lm83_update_device(dev);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
@@ -208,8 +219,64 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp, NULL, 8);
static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp,
set_temp, 8);
static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp, NULL, 8);
+
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(temp4_input_fault, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 15);
+/* Raw alarm file for compatibility */
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *lm83_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group lm83_group = {
+ .attrs = lm83_attributes,
+};
+
+static struct attribute *lm83_attributes_opt[] = {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm83_group_opt = {
+ .attrs = lm83_attributes_opt,
+};
+
/*
* Real code
*/
@@ -318,59 +385,32 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_free;
/*
- * Initialize the LM83 chip
- * (Nothing to do for this one.)
- */
-
- /* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- goto exit_detach;
- }
-
- /*
+ * Register sysfs hooks
* The LM82 can only monitor one external diode which is
* at the same register as the LM83 temp3 entry - so we
* declare 1 and 3 common, and then 2 and 4 only for the LM83.
*/
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_input.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_max.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_crit.dev_attr);
-
- device_create_file(&new_client->dev, &dev_attr_alarms);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm83_group)))
+ goto exit_detach;
if (kind == lm83) {
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp4_input.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp4_max.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp4_crit.dev_attr);
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &lm83_group_opt)))
+ goto exit_remove_files;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &lm83_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm83_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -385,6 +425,8 @@ static int lm83_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm83_group);
+ sysfs_remove_group(&client->dev.kobj, &lm83_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 342e9663119..2c3293cf69d 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -1025,6 +1025,89 @@ static int lm85_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm85_detect);
}
+static struct attribute *lm85_attributes[] = {
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan4_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan4_min.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm3.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm2_enable.attr,
+ &dev_attr_pwm3_enable.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp3_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_pwm1_auto_channels.attr,
+ &dev_attr_pwm2_auto_channels.attr,
+ &dev_attr_pwm3_auto_channels.attr,
+ &dev_attr_pwm1_auto_pwm_min.attr,
+ &dev_attr_pwm2_auto_pwm_min.attr,
+ &dev_attr_pwm3_auto_pwm_min.attr,
+ &dev_attr_pwm1_auto_pwm_minctl.attr,
+ &dev_attr_pwm2_auto_pwm_minctl.attr,
+ &dev_attr_pwm3_auto_pwm_minctl.attr,
+ &dev_attr_pwm1_auto_pwm_freq.attr,
+ &dev_attr_pwm2_auto_pwm_freq.attr,
+ &dev_attr_pwm3_auto_pwm_freq.attr,
+ &dev_attr_temp1_auto_temp_off.attr,
+ &dev_attr_temp2_auto_temp_off.attr,
+ &dev_attr_temp3_auto_temp_off.attr,
+ &dev_attr_temp1_auto_temp_min.attr,
+ &dev_attr_temp2_auto_temp_min.attr,
+ &dev_attr_temp3_auto_temp_min.attr,
+ &dev_attr_temp1_auto_temp_max.attr,
+ &dev_attr_temp2_auto_temp_max.attr,
+ &dev_attr_temp3_auto_temp_max.attr,
+ &dev_attr_temp1_auto_temp_crit.attr,
+ &dev_attr_temp2_auto_temp_crit.attr,
+ &dev_attr_temp3_auto_temp_crit.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm85_group = {
+ .attrs = lm85_attributes,
+};
+
+static struct attribute *lm85_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm85_group_opt = {
+ .attrs = lm85_attributes_opt,
+};
+
static int lm85_detect(struct i2c_adapter *adapter, int address,
int kind)
{
@@ -1163,87 +1246,33 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
lm85_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm85_group)))
goto ERROR2;
- }
-
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan3_input);
- device_create_file(&new_client->dev, &dev_attr_fan4_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan3_min);
- device_create_file(&new_client->dev, &dev_attr_fan4_min);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_pwm3);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm3_enable);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_vrm);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_channels);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_channels);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_channels);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_min);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_min);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_min);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_freq);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_freq);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_freq);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_off);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_off);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_off);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_crit);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_crit);
/* The ADT7463 has an optional VRM 10 mode where pin 21 is used
as a sixth digital VID input rather than an analog input. */
data->vid = lm85_read_value(new_client, LM85_REG_VID);
- if (!(kind == adt7463 && (data->vid & 0x80))) {
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
+ if (!(kind == adt7463 && (data->vid & 0x80)))
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto ERROR3;
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR3;
}
return 0;
/* Error out and cleanup code */
+ ERROR3:
+ sysfs_remove_group(&new_client->dev.kobj, &lm85_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm85_group_opt);
ERROR2:
i2c_detach_client(new_client);
ERROR1:
@@ -1256,6 +1285,8 @@ static int lm85_detach_client(struct i2c_client *client)
{
struct lm85_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm85_group);
+ sysfs_remove_group(&client->dev.kobj, &lm85_group_opt);
i2c_detach_client(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index e6c1b638c97..3ce825489e3 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -542,6 +542,78 @@ static int lm87_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm87_detect);
}
+static struct attribute *lm87_attributes[] = {
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_crit.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_aout_output.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm87_group = {
+ .attrs = lm87_attributes,
+};
+
+static struct attribute *lm87_attributes_opt[] = {
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+
+ &dev_attr_in7_input.attr,
+ &dev_attr_in7_min.attr,
+ &dev_attr_in7_max.attr,
+
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_min.attr,
+ &dev_attr_temp3_crit.attr,
+
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm87_group_opt = {
+ .attrs = lm87_attributes_opt,
+};
+
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
@@ -609,77 +681,90 @@ static int lm87_detect(struct i2c_adapter *adapter, int address, int kind)
data->in_scale[7] = 1875;
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm87_group)))
goto exit_detach;
- }
-
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
if (data->channel & CHAN_NO_FAN(0)) {
- device_create_file(&new_client->dev, &dev_attr_in6_input);
- device_create_file(&new_client->dev, &dev_attr_in6_min);
- device_create_file(&new_client->dev, &dev_attr_in6_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in6_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_max)))
+ goto exit_remove;
} else {
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_div)))
+ goto exit_remove;
}
+
if (data->channel & CHAN_NO_FAN(1)) {
- device_create_file(&new_client->dev, &dev_attr_in7_input);
- device_create_file(&new_client->dev, &dev_attr_in7_min);
- device_create_file(&new_client->dev, &dev_attr_in7_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in7_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in7_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in7_max)))
+ goto exit_remove;
} else {
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_div)))
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit);
-
if (data->channel & CHAN_TEMP3) {
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_crit);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_crit)))
+ goto exit_remove;
} else {
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in0_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in0_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in0_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_max)))
+ goto exit_remove;
}
if (!(data->channel & CHAN_NO_VID)) {
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_vrm);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_cpu0_vid))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_vrm)))
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_aout_output);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
+ }
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm87_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -732,6 +817,8 @@ static int lm87_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm87_group);
+ sysfs_remove_group(&client->dev.kobj, &lm87_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index d9eeaf7585b..6882ce75fee 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1,7 +1,7 @@
/*
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm83 driver. The LM90 is a sensor chip made by National
* Semiconductor. It reports up to two temperatures (its own plus up to
@@ -79,6 +79,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -327,6 +328,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
return sprintf(buf, "%d\n", data->alarms);
}
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm90_data *data = lm90_update_device(dev);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
@@ -344,8 +355,45 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
set_temphyst, 3);
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4);
+
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+/* Raw alarm file for compatibility */
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *lm90_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group lm90_group = {
+ .attrs = lm90_attributes,
+};
+
/* pec used for ADM1032 only */
static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
char *buf)
@@ -569,39 +617,25 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
lm90_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group)))
+ goto exit_detach;
+ if (new_client->flags & I2C_CLIENT_PEC) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pec)))
+ goto exit_remove_files;
+ }
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
- if (new_client->flags & I2C_CLIENT_PEC)
- device_create_file(&new_client->dev, &dev_attr_pec);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &lm90_group);
+ device_remove_file(&new_client->dev, &dev_attr_pec);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -634,6 +668,8 @@ static int lm90_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm90_group);
+ device_remove_file(&client->dev, &dev_attr_pec);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 197f77226dc..30b536333f1 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -288,6 +288,23 @@ static int max6635_check(struct i2c_client *client)
return 1;
}
+static struct attribute *lm92_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp1_crit_hyst.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_min_hyst.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm92_group = {
+ .attrs = lm92_attributes,
+};
+
/* The following function does more than just detection. If detection
succeeds, it also registers the new chip. */
static int lm92_detect(struct i2c_adapter *adapter, int address, int kind)
@@ -359,23 +376,19 @@ static int lm92_detect(struct i2c_adapter *adapter, int address, int kind)
lm92_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm92_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm92_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -397,6 +410,7 @@ static int lm92_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm92_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index b4135b5971f..2f58f651f03 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -34,6 +34,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
@@ -172,6 +173,22 @@ static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2,
set_temp_hyst2);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *max1619_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_crit.attr,
+ &dev_attr_temp2_crit_hyst.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group max1619_group = {
+ .attrs = max1619_attributes,
+};
+
/*
* Real code
*/
@@ -273,22 +290,19 @@ static int max1619_detect(struct i2c_adapter *adapter, int address, int kind)
max1619_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &max1619_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &max1619_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -318,6 +332,7 @@ static int max1619_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &max1619_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index ae05e483a77..3b8b81984ad 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -238,6 +238,7 @@ static struct pc87360_data *pc87360_update_device(struct device *dev);
static struct i2c_driver pc87360_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "pc87360",
},
.attach_adapter = pc87360_detect,
@@ -327,6 +328,12 @@ static struct sensor_device_attribute fan_min[] = {
SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2),
};
+#define FAN_UNIT_ATTRS(X) \
+ &fan_input[X].dev_attr.attr, \
+ &fan_status[X].dev_attr.attr, \
+ &fan_div[X].dev_attr.attr, \
+ &fan_min[X].dev_attr.attr
+
static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
@@ -359,6 +366,19 @@ static struct sensor_device_attribute pwm[] = {
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2),
};
+static struct attribute * pc8736x_fan_attr_array[] = {
+ FAN_UNIT_ATTRS(0),
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2),
+ &pwm[0].dev_attr.attr,
+ &pwm[1].dev_attr.attr,
+ &pwm[2].dev_attr.attr,
+ NULL
+};
+static const struct attribute_group pc8736x_fan_group = {
+ .attrs = pc8736x_fan_attr_array,
+};
+
static ssize_t show_in_input(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
@@ -471,6 +491,61 @@ static struct sensor_device_attribute in_max[] = {
SENSOR_ATTR(in10_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 10),
};
+#define VIN_UNIT_ATTRS(X) \
+ &in_input[X].dev_attr.attr, \
+ &in_status[X].dev_attr.attr, \
+ &in_min[X].dev_attr.attr, \
+ &in_max[X].dev_attr.attr
+
+static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
+static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", data->vrm);
+}
+static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pc87360_data *data = i2c_get_clientdata(client);
+ data->vrm = simple_strtoul(buf, NULL, 10);
+ return count;
+}
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+
+static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", data->in_alarms);
+}
+static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL);
+
+static struct attribute *pc8736x_vin_attr_array[] = {
+ VIN_UNIT_ATTRS(0),
+ VIN_UNIT_ATTRS(1),
+ VIN_UNIT_ATTRS(2),
+ VIN_UNIT_ATTRS(3),
+ VIN_UNIT_ATTRS(4),
+ VIN_UNIT_ATTRS(5),
+ VIN_UNIT_ATTRS(6),
+ VIN_UNIT_ATTRS(7),
+ VIN_UNIT_ATTRS(8),
+ VIN_UNIT_ATTRS(9),
+ VIN_UNIT_ATTRS(10),
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms_in.attr,
+ NULL
+};
+static const struct attribute_group pc8736x_vin_group = {
+ .attrs = pc8736x_vin_attr_array,
+};
+
static ssize_t show_therm_input(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
@@ -589,33 +664,22 @@ static struct sensor_device_attribute therm_crit[] = {
show_therm_crit, set_therm_crit, 2+11),
};
-static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct pc87360_data *data = pc87360_update_device(dev);
- return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
-}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
-
-static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct pc87360_data *data = pc87360_update_device(dev);
- return sprintf(buf, "%u\n", data->vrm);
-}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct pc87360_data *data = i2c_get_clientdata(client);
- data->vrm = simple_strtoul(buf, NULL, 10);
- return count;
-}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
-
-static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct pc87360_data *data = pc87360_update_device(dev);
- return sprintf(buf, "%u\n", data->in_alarms);
-}
-static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL);
+#define THERM_UNIT_ATTRS(X) \
+ &therm_input[X].dev_attr.attr, \
+ &therm_status[X].dev_attr.attr, \
+ &therm_min[X].dev_attr.attr, \
+ &therm_max[X].dev_attr.attr, \
+ &therm_crit[X].dev_attr.attr
+
+static struct attribute * pc8736x_therm_attr_array[] = {
+ THERM_UNIT_ATTRS(0),
+ THERM_UNIT_ATTRS(1),
+ THERM_UNIT_ATTRS(2),
+ NULL
+};
+static const struct attribute_group pc8736x_therm_group = {
+ .attrs = pc8736x_therm_attr_array,
+};
static ssize_t show_temp_input(struct device *dev, struct device_attribute *devattr, char *buf)
{
@@ -735,6 +799,25 @@ static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *att
}
static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL);
+#define TEMP_UNIT_ATTRS(X) \
+ &temp_input[X].dev_attr.attr, \
+ &temp_status[X].dev_attr.attr, \
+ &temp_min[X].dev_attr.attr, \
+ &temp_max[X].dev_attr.attr, \
+ &temp_crit[X].dev_attr.attr
+
+static struct attribute * pc8736x_temp_attr_array[] = {
+ TEMP_UNIT_ATTRS(0),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+ /* include the few miscellaneous atts here */
+ &dev_attr_alarms_temp.attr,
+ NULL
+};
+static const struct attribute_group pc8736x_temp_group = {
+ .attrs = pc8736x_temp_attr_array,
+};
+
/*
* Device detection, registration and update
*/
@@ -935,60 +1018,69 @@ static int pc87360_detect(struct i2c_adapter *adapter)
pc87360_init_client(client, use_thermistors);
}
- /* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ /* Register all-or-nothing sysfs groups */
+
+ if (data->innr &&
+ (err = sysfs_create_group(&client->dev.kobj,
+ &pc8736x_vin_group)))
goto ERROR3;
- }
- if (data->innr) {
- for (i = 0; i < 11; i++) {
- device_create_file(dev, &in_input[i].dev_attr);
- device_create_file(dev, &in_min[i].dev_attr);
- device_create_file(dev, &in_max[i].dev_attr);
- device_create_file(dev, &in_status[i].dev_attr);
- }
- device_create_file(dev, &dev_attr_cpu0_vid);
- device_create_file(dev, &dev_attr_vrm);
- device_create_file(dev, &dev_attr_alarms_in);
- }
+ if (data->innr == 14 &&
+ (err = sysfs_create_group(&client->dev.kobj,
+ &pc8736x_therm_group)))
+ goto ERROR3;
+
+ /* create device attr-files for varying sysfs groups */
if (data->tempnr) {
for (i = 0; i < data->tempnr; i++) {
- device_create_file(dev, &temp_input[i].dev_attr);
- device_create_file(dev, &temp_min[i].dev_attr);
- device_create_file(dev, &temp_max[i].dev_attr);
- device_create_file(dev, &temp_crit[i].dev_attr);
- device_create_file(dev, &temp_status[i].dev_attr);
- }
- device_create_file(dev, &dev_attr_alarms_temp);
- }
-
- if (data->innr == 14) {
- for (i = 0; i < 3; i++) {
- device_create_file(dev, &therm_input[i].dev_attr);
- device_create_file(dev, &therm_min[i].dev_attr);
- device_create_file(dev, &therm_max[i].dev_attr);
- device_create_file(dev, &therm_crit[i].dev_attr);
- device_create_file(dev, &therm_status[i].dev_attr);
+ if ((err = device_create_file(dev,
+ &temp_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_min[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_max[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_crit[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_status[i].dev_attr)))
+ goto ERROR3;
}
+ if ((err = device_create_file(dev, &dev_attr_alarms_temp)))
+ goto ERROR3;
}
for (i = 0; i < data->fannr; i++) {
- if (FAN_CONFIG_MONITOR(data->fan_conf, i)) {
- device_create_file(dev, &fan_input[i].dev_attr);
- device_create_file(dev, &fan_min[i].dev_attr);
- device_create_file(dev, &fan_div[i].dev_attr);
- device_create_file(dev, &fan_status[i].dev_attr);
- }
- if (FAN_CONFIG_CONTROL(data->fan_conf, i))
- device_create_file(dev, &pwm[i].dev_attr);
+ if (FAN_CONFIG_MONITOR(data->fan_conf, i)
+ && ((err = device_create_file(dev,
+ &fan_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &fan_min[i].dev_attr))
+ || (err = device_create_file(dev,
+ &fan_div[i].dev_attr))
+ || (err = device_create_file(dev,
+ &fan_status[i].dev_attr))))
+ goto ERROR3;
+
+ if (FAN_CONFIG_CONTROL(data->fan_conf, i)
+ && (err = device_create_file(dev, &pwm[i].dev_attr)))
+ goto ERROR3;
}
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR3;
+ }
return 0;
ERROR3:
+ /* can still remove groups whose members were added individually */
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group);
+
i2c_detach_client(client);
ERROR2:
for (i = 0; i < 3; i++) {
@@ -1008,6 +1100,11 @@ static int pc87360_detach_client(struct i2c_client *client)
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group);
+
if ((i = i2c_detach_client(client)))
return i;
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c
index 063f71c5f07..95a4b5d9eaf 100644
--- a/drivers/hwmon/sis5595.c
+++ b/drivers/hwmon/sis5595.c
@@ -61,6 +61,7 @@
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
@@ -200,6 +201,7 @@ static void sis5595_init_client(struct i2c_client *client);
static struct i2c_driver sis5595_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "sis5595",
},
.attach_adapter = sis5595_detect,
@@ -472,6 +474,50 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+static struct attribute *sis5595_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group sis5595_group = {
+ .attrs = sis5595_attributes,
+};
+
+static struct attribute *sis5595_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ NULL
+};
+
+static const struct attribute_group sis5595_group_opt = {
+ .attrs = sis5595_attributes_opt,
+};
/* This is called when the module is loaded */
static int sis5595_detect(struct i2c_adapter *adapter)
@@ -565,43 +611,37 @@ static int sis5595_detect(struct i2c_adapter *adapter)
}
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &sis5595_group)))
+ goto exit_detach;
+ if (data->maxins == 4) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto exit_remove_files;
+ } else {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_temp1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp1_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp1_max_hyst)))
+ goto exit_remove_files;
+ }
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- if (data->maxins == 4) {
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- }
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- if (data->maxins == 3) {
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- }
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &sis5595_group);
+ sysfs_remove_group(&new_client->dev.kobj, &sis5595_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -618,6 +658,8 @@ static int sis5595_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &sis5595_group);
+ sysfs_remove_group(&client->dev.kobj, &sis5595_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c
index b6086186d22..72b0e2d8650 100644
--- a/drivers/hwmon/smsc47b397.c
+++ b/drivers/hwmon/smsc47b397.c
@@ -176,9 +176,6 @@ sysfs_temp(2);
sysfs_temp(3);
sysfs_temp(4);
-#define device_create_file_temp(client, num) \
- device_create_file(&client->dev, &dev_attr_temp##num##_input)
-
/* FAN: 1 RPM/bit
REG: count of 90kHz pulses / revolution */
static int fan_from_reg(u16 reg)
@@ -205,8 +202,22 @@ sysfs_fan(2);
sysfs_fan(3);
sysfs_fan(4);
-#define device_create_file_fan(client, num) \
- device_create_file(&client->dev, &dev_attr_fan##num##_input)
+static struct attribute *smsc47b397_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp4_input.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan4_input.attr,
+
+ NULL
+};
+
+static const struct attribute_group smsc47b397_group = {
+ .attrs = smsc47b397_attributes,
+};
static int smsc47b397_detach_client(struct i2c_client *client)
{
@@ -214,6 +225,7 @@ static int smsc47b397_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &smsc47b397_group);
if ((err = i2c_detach_client(client)))
return err;
@@ -228,6 +240,7 @@ static int smsc47b397_detect(struct i2c_adapter *adapter);
static struct i2c_driver smsc47b397_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "smsc47b397",
},
.attach_adapter = smsc47b397_detect,
@@ -267,24 +280,19 @@ static int smsc47b397_detect(struct i2c_adapter *adapter)
if ((err = i2c_attach_client(new_client)))
goto error_free;
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &smsc47b397_group)))
+ goto error_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto error_detach;
+ goto error_remove;
}
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- device_create_file_temp(new_client, 3);
- device_create_file_temp(new_client, 4);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
- device_create_file_fan(new_client, 4);
-
return 0;
+error_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &smsc47b397_group);
error_detach:
i2c_detach_client(new_client);
error_free:
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c
index 825e8f72698..47132fd26b1 100644
--- a/drivers/hwmon/smsc47m1.c
+++ b/drivers/hwmon/smsc47m1.c
@@ -35,6 +35,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
/* Address is autodetected, there is no default value */
@@ -128,6 +129,7 @@ static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
static struct i2c_driver smsc47m1_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "smsc47m1",
},
.attach_adapter = smsc47m1_detect,
@@ -346,6 +348,30 @@ fan_present(2);
static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
+/* Almost all sysfs files may or may not be created depending on the chip
+ setup so we create them individually. It is still convenient to define a
+ group to remove them all at once. */
+static struct attribute *smsc47m1_attributes[] = {
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm2_enable.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group = {
+ .attrs = smsc47m1_attributes,
+};
+
static int __init smsc47m1_find(unsigned short *addr)
{
u8 val;
@@ -428,7 +454,8 @@ static int smsc47m1_detect(struct i2c_adapter *adapter)
pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
== 0x04;
if (!(fan1 || fan2 || pwm1 || pwm2)) {
- dev_warn(&new_client->dev, "Device is not configured, will not use\n");
+ dev_warn(&adapter->dev, "Device at 0x%x is not configured, "
+ "will not use\n", new_client->addr);
err = -ENODEV;
goto error_free;
}
@@ -445,46 +472,62 @@ static int smsc47m1_detect(struct i2c_adapter *adapter)
smsc47m1_update_device(&new_client->dev, 1);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- goto error_detach;
- }
-
if (fan1) {
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_div)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
"skipping\n");
if (fan2) {
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_div)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
"skipping\n");
if (pwm1) {
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pwm1))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_pwm1_enable)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
"skipping\n");
if (pwm2) {
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pwm2))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_pwm2_enable)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
"skipping\n");
- device_create_file(&new_client->dev, &dev_attr_alarms);
+ if ((err = device_create_file(&new_client->dev, &dev_attr_alarms)))
+ goto error_remove_files;
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto error_remove_files;
+ }
return 0;
-error_detach:
+error_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group);
i2c_detach_client(new_client);
error_free:
kfree(data);
@@ -499,6 +542,7 @@ static int smsc47m1_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m1_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
index bdc4570acf9..a6833f43739 100644
--- a/drivers/hwmon/smsc47m192.c
+++ b/drivers/hwmon/smsc47m192.c
@@ -30,6 +30,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon-vid.h>
#include <linux/err.h>
+#include <linux/sysfs.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
@@ -370,6 +371,75 @@ static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 0x0200);
static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 0x0400);
static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 0x0800);
+static struct attribute *smsc47m192_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_input_fault.dev_attr.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m192_group = {
+ .attrs = smsc47m192_attributes,
+};
+
+static struct attribute *smsc47m192_attributes_in4[] = {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m192_group_in4 = {
+ .attrs = smsc47m192_attributes_in4,
+};
+
/* This function is called when:
* smsc47m192_driver is inserted (when this module is loaded), for each
available adapter
@@ -471,80 +541,28 @@ static int smsc47m192_detect(struct i2c_adapter *adapter, int address,
smsc47m192_init_client(client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&client->dev.kobj, &smsc47m192_group)))
goto exit_detach;
- }
-
- device_create_file(&client->dev, &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in0_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_alarm.dev_attr);
/* Pin 110 is either in4 (+12V) or VID4 */
config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG);
if (!(config & 0x20)) {
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_alarm.dev_attr);
+ if ((err = sysfs_create_group(&client->dev.kobj,
+ &smsc47m192_group_in4)))
+ goto exit_remove_files;
+ }
+
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
- device_create_file(&client->dev, &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in5_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp1_offset.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp2_offset.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_alarm.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp2_input_fault.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp3_offset.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_alarm.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp3_input_fault.dev_attr);
- device_create_file(&client->dev, &dev_attr_cpu0_vid);
- device_create_file(&client->dev, &dev_attr_vrm);
return 0;
+exit_remove_files:
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4);
exit_detach:
i2c_detach_client(client);
exit_free:
@@ -559,6 +577,8 @@ static int smsc47m192_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c
index 166298f1f19..f8acada0537 100644
--- a/drivers/hwmon/via686a.c
+++ b/drivers/hwmon/via686a.c
@@ -40,6 +40,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
@@ -570,10 +571,53 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *via686a_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ &dev_attr_temp3_max_hyst.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group via686a_group = {
+ .attrs = via686a_attributes,
+};
+
/* The driver. I choose to use type i2c_driver, as at is identical to both
smbus_driver and isa_driver, and clients could be of either kind */
static struct i2c_driver via686a_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "via686a",
},
.attach_adapter = via686a_detect,
@@ -649,46 +693,19 @@ static int via686a_detect(struct i2c_adapter *adapter)
via686a_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &via686a_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &via686a_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -704,6 +721,7 @@ static int via686a_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &via686a_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c
new file mode 100644
index 00000000000..25cc56003d7
--- /dev/null
+++ b/drivers/hwmon/vt1211.c
@@ -0,0 +1,1355 @@
+/*
+ * vt1211.c - driver for the VIA VT1211 Super-I/O chip integrated hardware
+ * monitoring features
+ * Copyright (C) 2006 Juerg Haefliger <juergh@gmail.com>
+ *
+ * This driver is based on the driver for kernel 2.4 by Mark D. Studebaker
+ * and its port to kernel 2.6 by Lars Ekman.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <asm/io.h>
+
+static int uch_config = -1;
+module_param(uch_config, int, 0);
+MODULE_PARM_DESC(uch_config, "Initialize the universal channel configuration");
+
+static int int_mode = -1;
+module_param(int_mode, int, 0);
+MODULE_PARM_DESC(int_mode, "Force the temperature interrupt mode");
+
+static struct platform_device *pdev;
+
+#define DRVNAME "vt1211"
+
+/* ---------------------------------------------------------------------
+ * Registers
+ *
+ * The sensors are defined as follows.
+ *
+ * Sensor Voltage Mode Temp Mode Notes (from the datasheet)
+ * -------- ------------ --------- --------------------------
+ * Reading 1 temp1 Intel thermal diode
+ * Reading 3 temp2 Internal thermal diode
+ * UCH1/Reading2 in0 temp3 NTC type thermistor
+ * UCH2 in1 temp4 +2.5V
+ * UCH3 in2 temp5 VccP
+ * UCH4 in3 temp6 +5V
+ * UCH5 in4 temp7 +12V
+ * 3.3V in5 Internal VDD (+3.3V)
+ *
+ * --------------------------------------------------------------------- */
+
+/* Voltages (in) numbered 0-5 (ix) */
+#define VT1211_REG_IN(ix) (0x21 + (ix))
+#define VT1211_REG_IN_MIN(ix) ((ix) == 0 ? 0x3e : 0x2a + 2 * (ix))
+#define VT1211_REG_IN_MAX(ix) ((ix) == 0 ? 0x3d : 0x29 + 2 * (ix))
+
+/* Temperatures (temp) numbered 0-6 (ix) */
+static u8 regtemp[] = {0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25};
+static u8 regtempmax[] = {0x39, 0x1d, 0x3d, 0x2b, 0x2d, 0x2f, 0x31};
+static u8 regtemphyst[] = {0x3a, 0x1e, 0x3e, 0x2c, 0x2e, 0x30, 0x32};
+
+/* Fans numbered 0-1 (ix) */
+#define VT1211_REG_FAN(ix) (0x29 + (ix))
+#define VT1211_REG_FAN_MIN(ix) (0x3b + (ix))
+#define VT1211_REG_FAN_DIV 0x47
+
+/* PWMs numbered 0-1 (ix) */
+/* Auto points numbered 0-3 (ap) */
+#define VT1211_REG_PWM(ix) (0x60 + (ix))
+#define VT1211_REG_PWM_CLK 0x50
+#define VT1211_REG_PWM_CTL 0x51
+#define VT1211_REG_PWM_AUTO_TEMP(ap) (0x55 - (ap))
+#define VT1211_REG_PWM_AUTO_PWM(ix, ap) (0x58 + 2 * (ix) - (ap))
+
+/* Miscellaneous registers */
+#define VT1211_REG_CONFIG 0x40
+#define VT1211_REG_ALARM1 0x41
+#define VT1211_REG_ALARM2 0x42
+#define VT1211_REG_VID 0x45
+#define VT1211_REG_UCH_CONFIG 0x4a
+#define VT1211_REG_TEMP1_CONFIG 0x4b
+#define VT1211_REG_TEMP2_CONFIG 0x4c
+
+/* In, temp & fan alarm bits */
+static const u8 bitalarmin[] = {11, 0, 1, 3, 8, 2, 9};
+static const u8 bitalarmtemp[] = {4, 15, 11, 0, 1, 3, 8};
+static const u8 bitalarmfan[] = {6, 7};
+
+/* ---------------------------------------------------------------------
+ * Data structures and manipulation thereof
+ * --------------------------------------------------------------------- */
+
+struct vt1211_data {
+ unsigned short addr;
+ const char *name;
+ struct class_device *class_dev;
+
+ struct mutex update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Register values */
+ u8 in[6];
+ u8 in_max[6];
+ u8 in_min[6];
+ u8 temp[7];
+ u8 temp_max[7];
+ u8 temp_hyst[7];
+ u8 fan[2];
+ u8 fan_min[2];
+ u8 fan_div[2];
+ u8 fan_ctl;
+ u8 pwm[2];
+ u8 pwm_ctl[2];
+ u8 pwm_clk;
+ u8 pwm_auto_temp[4];
+ u8 pwm_auto_pwm[2][4];
+ u8 vid; /* Read once at init time */
+ u8 vrm;
+ u8 uch_config; /* Read once at init time */
+ u16 alarms;
+};
+
+/* ix = [0-5] */
+#define ISVOLT(ix, uch_config) ((ix) > 4 ? 1 : \
+ !(((uch_config) >> ((ix) + 2)) & 1))
+
+/* ix = [0-6] */
+#define ISTEMP(ix, uch_config) ((ix) < 2 ? 1 : \
+ ((uch_config) >> (ix)) & 1)
+
+/* in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the
+ driver according to the VT1211 BIOS porting guide */
+#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \
+ (((reg) - 3) * 15882 + 479) / 958 : \
+ (((reg) - 3) * 10000 + 479) / 958)
+#define IN_TO_REG(ix, val) (SENSORS_LIMIT((ix) == 5 ? \
+ ((val) * 958 + 7941) / 15882 + 3 : \
+ ((val) * 958 + 5000) / 10000 + 3, 0, 255))
+
+/* temp1 (ix = 0) is an intel thermal diode which is scaled in user space.
+ temp2 (ix = 1) is the internal temp diode so it's scaled in the driver
+ according to some measurements that I took on an EPIA M10000.
+ temp3-7 are thermistor based so the driver returns the voltage measured at
+ the pin (range 0V - 2.2V). */
+#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \
+ (ix) == 1 ? (reg) < 51 ? 0 : \
+ ((reg) - 51) * 1000 : \
+ ((253 - (reg)) * 2200 + 105) / 210)
+#define TEMP_TO_REG(ix, val) SENSORS_LIMIT( \
+ ((ix) == 0 ? ((val) + 500) / 1000 : \
+ (ix) == 1 ? ((val) + 500) / 1000 + 51 : \
+ 253 - ((val) * 210 + 1100) / 2200), 0, 255)
+
+#define DIV_FROM_REG(reg) (1 << (reg))
+
+#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \
+ 1310720 / (reg) / DIV_FROM_REG(div))
+#define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \
+ SENSORS_LIMIT((1310720 / (val) / \
+ DIV_FROM_REG(div)), 1, 254))
+
+/* ---------------------------------------------------------------------
+ * Super-I/O constants and functions
+ * --------------------------------------------------------------------- */
+
+/* Configuration & data index port registers */
+#define SIO_REG_CIP 0x2e
+#define SIO_REG_DIP 0x2f
+
+/* Configuration registers */
+#define SIO_VT1211_LDN 0x07 /* logical device number */
+#define SIO_VT1211_DEVID 0x20 /* device ID */
+#define SIO_VT1211_DEVREV 0x21 /* device revision */
+#define SIO_VT1211_ACTIVE 0x30 /* HW monitor active */
+#define SIO_VT1211_BADDR 0x60 /* base I/O address */
+#define SIO_VT1211_ID 0x3c /* VT1211 device ID */
+
+/* VT1211 logical device numbers */
+#define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */
+
+static inline void superio_outb(int reg, int val)
+{
+ outb(reg, SIO_REG_CIP);
+ outb(val, SIO_REG_DIP);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, SIO_REG_CIP);
+ return inb(SIO_REG_DIP);
+}
+
+static inline void superio_select(int ldn)
+{
+ outb(SIO_VT1211_LDN, SIO_REG_CIP);
+ outb(ldn, SIO_REG_DIP);
+}
+
+static inline void superio_enter(void)
+{
+ outb(0x87, SIO_REG_CIP);
+ outb(0x87, SIO_REG_CIP);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0xaa, SIO_REG_CIP);
+}
+
+/* ---------------------------------------------------------------------
+ * Device I/O access
+ * --------------------------------------------------------------------- */
+
+static inline u8 vt1211_read8(struct vt1211_data *data, u8 reg)
+{
+ return inb(data->addr + reg);
+}
+
+static inline void vt1211_write8(struct vt1211_data *data, u8 reg, u8 val)
+{
+ outb(val, data->addr + reg);
+}
+
+static struct vt1211_data *vt1211_update_device(struct device *dev)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ int ix, val;
+
+ mutex_lock(&data->update_lock);
+
+ /* registers cache is refreshed after 1 second */
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ /* read VID */
+ data->vid = vt1211_read8(data, VT1211_REG_VID) & 0x1f;
+
+ /* voltage (in) registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
+ if (ISVOLT(ix, data->uch_config)) {
+ data->in[ix] = vt1211_read8(data,
+ VT1211_REG_IN(ix));
+ data->in_min[ix] = vt1211_read8(data,
+ VT1211_REG_IN_MIN(ix));
+ data->in_max[ix] = vt1211_read8(data,
+ VT1211_REG_IN_MAX(ix));
+ }
+ }
+
+ /* temp registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) {
+ if (ISTEMP(ix, data->uch_config)) {
+ data->temp[ix] = vt1211_read8(data,
+ regtemp[ix]);
+ data->temp_max[ix] = vt1211_read8(data,
+ regtempmax[ix]);
+ data->temp_hyst[ix] = vt1211_read8(data,
+ regtemphyst[ix]);
+ }
+ }
+
+ /* fan & pwm registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
+ data->fan[ix] = vt1211_read8(data,
+ VT1211_REG_FAN(ix));
+ data->fan_min[ix] = vt1211_read8(data,
+ VT1211_REG_FAN_MIN(ix));
+ data->pwm[ix] = vt1211_read8(data,
+ VT1211_REG_PWM(ix));
+ }
+ val = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (val >> 4) & 3;
+ data->fan_div[1] = (val >> 6) & 3;
+ data->fan_ctl = val & 0xf;
+
+ val = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = val & 0xf;
+ data->pwm_ctl[1] = (val >> 4) & 0xf;
+
+ data->pwm_clk = vt1211_read8(data, VT1211_REG_PWM_CLK);
+
+ /* pwm & temp auto point registers */
+ data->pwm_auto_pwm[0][1] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(0, 1));
+ data->pwm_auto_pwm[0][2] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(0, 2));
+ data->pwm_auto_pwm[1][1] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(1, 1));
+ data->pwm_auto_pwm[1][2] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(1, 2));
+ for (ix = 0; ix < ARRAY_SIZE(data->pwm_auto_temp); ix++) {
+ data->pwm_auto_temp[ix] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_TEMP(ix));
+ }
+
+ /* alarm registers */
+ data->alarms = (vt1211_read8(data, VT1211_REG_ALARM2) << 8) |
+ vt1211_read8(data, VT1211_REG_ALARM1);
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* ---------------------------------------------------------------------
+ * Voltage sysfs interfaces
+ * ix = [0-5]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_IN_INPUT 0
+#define SHOW_SET_IN_MIN 1
+#define SHOW_SET_IN_MAX 2
+#define SHOW_IN_ALARM 3
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_IN_INPUT:
+ res = IN_FROM_REG(ix, data->in[ix]);
+ break;
+ case SHOW_SET_IN_MIN:
+ res = IN_FROM_REG(ix, data->in_min[ix]);
+ break;
+ case SHOW_SET_IN_MAX:
+ res = IN_FROM_REG(ix, data->in_max[ix]);
+ break;
+ case SHOW_IN_ALARM:
+ res = (data->alarms >> bitalarmin[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SHOW_SET_IN_MIN:
+ data->in_min[ix] = IN_TO_REG(ix, val);
+ vt1211_write8(data, VT1211_REG_IN_MIN(ix), data->in_min[ix]);
+ break;
+ case SHOW_SET_IN_MAX:
+ data->in_max[ix] = IN_TO_REG(ix, val);
+ vt1211_write8(data, VT1211_REG_IN_MAX(ix), data->in_max[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Temperature sysfs interfaces
+ * ix = [0-6]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_TEMP_INPUT 0
+#define SHOW_SET_TEMP_MAX 1
+#define SHOW_SET_TEMP_MAX_HYST 2
+#define SHOW_TEMP_ALARM 3
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_TEMP_INPUT:
+ res = TEMP_FROM_REG(ix, data->temp[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX:
+ res = TEMP_FROM_REG(ix, data->temp_max[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX_HYST:
+ res = TEMP_FROM_REG(ix, data->temp_hyst[ix]);
+ break;
+ case SHOW_TEMP_ALARM:
+ res = (data->alarms >> bitalarmtemp[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SHOW_SET_TEMP_MAX:
+ data->temp_max[ix] = TEMP_TO_REG(ix, val);
+ vt1211_write8(data, regtempmax[ix],
+ data->temp_max[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX_HYST:
+ data->temp_hyst[ix] = TEMP_TO_REG(ix, val);
+ vt1211_write8(data, regtemphyst[ix],
+ data->temp_hyst[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Fan sysfs interfaces
+ * ix = [0-1]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_FAN_INPUT 0
+#define SHOW_SET_FAN_MIN 1
+#define SHOW_SET_FAN_DIV 2
+#define SHOW_FAN_ALARM 3
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_FAN_INPUT:
+ res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]);
+ break;
+ case SHOW_SET_FAN_MIN:
+ res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]);
+ break;
+ case SHOW_SET_FAN_DIV:
+ res = DIV_FROM_REG(data->fan_div[ix]);
+ break;
+ case SHOW_FAN_ALARM:
+ res = (data->alarms >> bitalarmfan[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+ int reg;
+
+ mutex_lock(&data->update_lock);
+
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (reg >> 4) & 3;
+ data->fan_div[1] = (reg >> 6) & 3;
+ data->fan_ctl = reg & 0xf;
+
+ switch (fn) {
+ case SHOW_SET_FAN_MIN:
+ data->fan_min[ix] = RPM_TO_REG(val, data->fan_div[ix]);
+ vt1211_write8(data, VT1211_REG_FAN_MIN(ix),
+ data->fan_min[ix]);
+ break;
+ case SHOW_SET_FAN_DIV:
+ switch (val) {
+ case 1: data->fan_div[ix] = 0; break;
+ case 2: data->fan_div[ix] = 1; break;
+ case 4: data->fan_div[ix] = 2; break;
+ case 8: data->fan_div[ix] = 3; break;
+ default:
+ count = -EINVAL;
+ dev_warn(dev, "fan div value %ld not "
+ "supported. Choose one of 1, 2, "
+ "4, or 8.\n", val);
+ goto EXIT;
+ }
+ vt1211_write8(data, VT1211_REG_FAN_DIV,
+ ((data->fan_div[1] << 6) |
+ (data->fan_div[0] << 4) |
+ data->fan_ctl));
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * PWM sysfs interfaces
+ * ix = [0-1]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_PWM 0
+#define SHOW_SET_PWM_ENABLE 1
+#define SHOW_SET_PWM_FREQ 2
+#define SHOW_SET_PWM_AUTO_CHANNELS_TEMP 3
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_PWM:
+ res = data->pwm[ix];
+ break;
+ case SHOW_SET_PWM_ENABLE:
+ res = ((data->pwm_ctl[ix] >> 3) & 1) ? 2 : 0;
+ break;
+ case SHOW_SET_PWM_FREQ:
+ res = 90000 >> (data->pwm_clk & 7);
+ break;
+ case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
+ res = (data->pwm_ctl[ix] & 7) + 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+ int tmp, reg;
+
+ mutex_lock(&data->update_lock);
+
+ switch (fn) {
+ case SHOW_SET_PWM_ENABLE:
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (reg >> 4) & 3;
+ data->fan_div[1] = (reg >> 6) & 3;
+ data->fan_ctl = reg & 0xf;
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+ switch (val) {
+ case 0:
+ data->pwm_ctl[ix] &= 7;
+ /* disable SmartGuardian if both PWM outputs are
+ * disabled */
+ if ((data->pwm_ctl[ix ^ 1] & 1) == 0) {
+ data->fan_ctl &= 0xe;
+ }
+ break;
+ case 2:
+ data->pwm_ctl[ix] |= 8;
+ data->fan_ctl |= 1;
+ break;
+ default:
+ count = -EINVAL;
+ dev_warn(dev, "pwm mode %ld not supported. "
+ "Choose one of 0 or 2.\n", val);
+ goto EXIT;
+ }
+ vt1211_write8(data, VT1211_REG_PWM_CTL,
+ ((data->pwm_ctl[1] << 4) |
+ data->pwm_ctl[0]));
+ vt1211_write8(data, VT1211_REG_FAN_DIV,
+ ((data->fan_div[1] << 6) |
+ (data->fan_div[0] << 4) |
+ data->fan_ctl));
+ break;
+ case SHOW_SET_PWM_FREQ:
+ val = 135000 / SENSORS_LIMIT(val, 135000 >> 7, 135000);
+ /* calculate tmp = log2(val) */
+ tmp = 0;
+ for (val >>= 1; val > 0; val >>= 1) {
+ tmp++;
+ }
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CLK);
+ data->pwm_clk = (reg & 0xf8) | tmp;
+ vt1211_write8(data, VT1211_REG_PWM_CLK, data->pwm_clk);
+ break;
+ case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
+ if ((val < 1) || (val > 7)) {
+ count = -EINVAL;
+ dev_warn(dev, "temp channel %ld not supported. "
+ "Choose a value between 1 and 7.\n", val);
+ goto EXIT;
+ }
+ if (!ISTEMP(val - 1, data->uch_config)) {
+ count = -EINVAL;
+ dev_warn(dev, "temp channel %ld is not available.\n",
+ val);
+ goto EXIT;
+ }
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+ data->pwm_ctl[ix] = (data->pwm_ctl[ix] & 8) | (val - 1);
+ vt1211_write8(data, VT1211_REG_PWM_CTL,
+ ((data->pwm_ctl[1] << 4) | data->pwm_ctl[0]));
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * PWM auto point definitions
+ * ix = [0-1]
+ * ap = [0-3]
+ * --------------------------------------------------------------------- */
+
+/*
+ * pwm[ix+1]_auto_point[ap+1]_temp mapping table:
+ * Note that there is only a single set of temp auto points that controls both
+ * PWM controllers. We still create 2 sets of sysfs files to make it look
+ * more consistent even though they map to the same registers.
+ *
+ * ix ap : description
+ * -------------------
+ * 0 0 : pwm1/2 off temperature (pwm_auto_temp[0])
+ * 0 1 : pwm1/2 low speed temperature (pwm_auto_temp[1])
+ * 0 2 : pwm1/2 high speed temperature (pwm_auto_temp[2])
+ * 0 3 : pwm1/2 full speed temperature (pwm_auto_temp[3])
+ * 1 0 : pwm1/2 off temperature (pwm_auto_temp[0])
+ * 1 1 : pwm1/2 low speed temperature (pwm_auto_temp[1])
+ * 1 2 : pwm1/2 high speed temperature (pwm_auto_temp[2])
+ * 1 3 : pwm1/2 full speed temperature (pwm_auto_temp[3])
+ */
+
+static ssize_t show_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7,
+ data->pwm_auto_temp[ap]));
+}
+
+static ssize_t set_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+ int reg;
+
+ mutex_lock(&data->update_lock);
+
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+
+ data->pwm_auto_temp[ap] = TEMP_TO_REG(data->pwm_ctl[ix] & 7, val);
+ vt1211_write8(data, VT1211_REG_PWM_AUTO_TEMP(ap),
+ data->pwm_auto_temp[ap]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * pwm[ix+1]_auto_point[ap+1]_pwm mapping table:
+ * Note that the PWM auto points 0 & 3 are hard-wired in the VT1211 and can't
+ * be changed.
+ *
+ * ix ap : description
+ * -------------------
+ * 0 0 : pwm1 off (pwm_auto_pwm[0][0], hard-wired to 0)
+ * 0 1 : pwm1 low speed duty cycle (pwm_auto_pwm[0][1])
+ * 0 2 : pwm1 high speed duty cycle (pwm_auto_pwm[0][2])
+ * 0 3 : pwm1 full speed (pwm_auto_pwm[0][3], hard-wired to 255)
+ * 1 0 : pwm2 off (pwm_auto_pwm[1][0], hard-wired to 0)
+ * 1 1 : pwm2 low speed duty cycle (pwm_auto_pwm[1][1])
+ * 1 2 : pwm2 high speed duty cycle (pwm_auto_pwm[1][2])
+ * 1 3 : pwm2 full speed (pwm_auto_pwm[1][3], hard-wired to 255)
+*/
+
+static ssize_t show_pwm_auto_point_pwm(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+
+ return sprintf(buf, "%d\n", data->pwm_auto_pwm[ix][ap]);
+}
+
+static ssize_t set_pwm_auto_point_pwm(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+
+ if ((val < 0) || (val > 255)) {
+ dev_err(dev, "pwm value %ld is out of range. "
+ "Choose a value between 0 and 255." , val);
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->pwm_auto_pwm[ix][ap] = val;
+ vt1211_write8(data, VT1211_REG_PWM_AUTO_PWM(ix, ap),
+ data->pwm_auto_pwm[ix][ap]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Miscellaneous sysfs interfaces (VRM, VID, name, and (legacy) alarms)
+ * --------------------------------------------------------------------- */
+
+static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ long val = simple_strtol(buf, NULL, 10);
+
+ data->vrm = val;
+
+ return count;
+}
+
+static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t show_alarms(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+/* ---------------------------------------------------------------------
+ * Device attribute structs
+ * --------------------------------------------------------------------- */
+
+#define SENSOR_ATTR_IN_INPUT(ix) \
+ SENSOR_ATTR_2(in##ix##_input, S_IRUGO, \
+ show_in, NULL, SHOW_IN_INPUT, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_input[] = {
+ SENSOR_ATTR_IN_INPUT(0),
+ SENSOR_ATTR_IN_INPUT(1),
+ SENSOR_ATTR_IN_INPUT(2),
+ SENSOR_ATTR_IN_INPUT(3),
+ SENSOR_ATTR_IN_INPUT(4),
+ SENSOR_ATTR_IN_INPUT(5),
+};
+
+#define SENSOR_ATTR_IN_MIN(ix) \
+ SENSOR_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SHOW_SET_IN_MIN, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_min[] = {
+ SENSOR_ATTR_IN_MIN(0),
+ SENSOR_ATTR_IN_MIN(1),
+ SENSOR_ATTR_IN_MIN(2),
+ SENSOR_ATTR_IN_MIN(3),
+ SENSOR_ATTR_IN_MIN(4),
+ SENSOR_ATTR_IN_MIN(5),
+};
+
+#define SENSOR_ATTR_IN_MAX(ix) \
+ SENSOR_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SHOW_SET_IN_MAX, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_max[] = {
+ SENSOR_ATTR_IN_MAX(0),
+ SENSOR_ATTR_IN_MAX(1),
+ SENSOR_ATTR_IN_MAX(2),
+ SENSOR_ATTR_IN_MAX(3),
+ SENSOR_ATTR_IN_MAX(4),
+ SENSOR_ATTR_IN_MAX(5),
+};
+
+#define SENSOR_ATTR_IN_ALARM(ix) \
+ SENSOR_ATTR_2(in##ix##_alarm, S_IRUGO, \
+ show_in, NULL, SHOW_IN_ALARM, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_alarm[] = {
+ SENSOR_ATTR_IN_ALARM(0),
+ SENSOR_ATTR_IN_ALARM(1),
+ SENSOR_ATTR_IN_ALARM(2),
+ SENSOR_ATTR_IN_ALARM(3),
+ SENSOR_ATTR_IN_ALARM(4),
+ SENSOR_ATTR_IN_ALARM(5),
+};
+
+#define SENSOR_ATTR_TEMP_INPUT(ix) \
+ SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \
+ show_temp, NULL, SHOW_TEMP_INPUT, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_input[] = {
+ SENSOR_ATTR_TEMP_INPUT(1),
+ SENSOR_ATTR_TEMP_INPUT(2),
+ SENSOR_ATTR_TEMP_INPUT(3),
+ SENSOR_ATTR_TEMP_INPUT(4),
+ SENSOR_ATTR_TEMP_INPUT(5),
+ SENSOR_ATTR_TEMP_INPUT(6),
+ SENSOR_ATTR_TEMP_INPUT(7),
+};
+
+#define SENSOR_ATTR_TEMP_MAX(ix) \
+ SENSOR_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_max[] = {
+ SENSOR_ATTR_TEMP_MAX(1),
+ SENSOR_ATTR_TEMP_MAX(2),
+ SENSOR_ATTR_TEMP_MAX(3),
+ SENSOR_ATTR_TEMP_MAX(4),
+ SENSOR_ATTR_TEMP_MAX(5),
+ SENSOR_ATTR_TEMP_MAX(6),
+ SENSOR_ATTR_TEMP_MAX(7),
+};
+
+#define SENSOR_ATTR_TEMP_MAX_HYST(ix) \
+ SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_max_hyst[] = {
+ SENSOR_ATTR_TEMP_MAX_HYST(1),
+ SENSOR_ATTR_TEMP_MAX_HYST(2),
+ SENSOR_ATTR_TEMP_MAX_HYST(3),
+ SENSOR_ATTR_TEMP_MAX_HYST(4),
+ SENSOR_ATTR_TEMP_MAX_HYST(5),
+ SENSOR_ATTR_TEMP_MAX_HYST(6),
+ SENSOR_ATTR_TEMP_MAX_HYST(7),
+};
+
+#define SENSOR_ATTR_TEMP_ALARM(ix) \
+ SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \
+ show_temp, NULL, SHOW_TEMP_ALARM, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_alarm[] = {
+ SENSOR_ATTR_TEMP_ALARM(1),
+ SENSOR_ATTR_TEMP_ALARM(2),
+ SENSOR_ATTR_TEMP_ALARM(3),
+ SENSOR_ATTR_TEMP_ALARM(4),
+ SENSOR_ATTR_TEMP_ALARM(5),
+ SENSOR_ATTR_TEMP_ALARM(6),
+ SENSOR_ATTR_TEMP_ALARM(7),
+};
+
+#define SENSOR_ATTR_FAN(ix) \
+ SENSOR_ATTR_2(fan##ix##_input, S_IRUGO, \
+ show_fan, NULL, SHOW_FAN_INPUT, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SHOW_SET_FAN_MIN, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_div, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SHOW_SET_FAN_DIV, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_alarm, S_IRUGO, \
+ show_fan, NULL, SHOW_FAN_ALARM, ix-1)
+
+#define SENSOR_ATTR_PWM(ix) \
+ SENSOR_ATTR_2(pwm##ix, S_IRUGO, \
+ show_pwm, NULL, SHOW_PWM, ix-1), \
+ SENSOR_ATTR_2(pwm##ix##_enable, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_ENABLE, ix-1), \
+ SENSOR_ATTR_2(pwm##ix##_auto_channels_temp, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_AUTO_CHANNELS_TEMP, ix-1)
+
+#define SENSOR_ATTR_PWM_FREQ(ix) \
+ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_FREQ, ix-1)
+
+#define SENSOR_ATTR_PWM_FREQ_RO(ix) \
+ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO, \
+ show_pwm, NULL, SHOW_SET_PWM_FREQ, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO | S_IWUSR, \
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO, \
+ show_pwm_auto_point_temp, NULL, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_PWM(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO | S_IWUSR, \
+ show_pwm_auto_point_pwm, set_pwm_auto_point_pwm, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO, \
+ show_pwm_auto_point_pwm, NULL, \
+ ap-1, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_fan_pwm[] = {
+ SENSOR_ATTR_FAN(1),
+ SENSOR_ATTR_FAN(2),
+ SENSOR_ATTR_PWM(1),
+ SENSOR_ATTR_PWM(2),
+ SENSOR_ATTR_PWM_FREQ(1),
+ SENSOR_ATTR_PWM_FREQ_RO(2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 4),
+};
+
+static struct device_attribute vt1211_sysfs_misc[] = {
+ __ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm),
+ __ATTR(cpu0_vid, S_IRUGO, show_vid, NULL),
+ __ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(alarms, S_IRUGO, show_alarms, NULL),
+};
+
+/* ---------------------------------------------------------------------
+ * Device registration and initialization
+ * --------------------------------------------------------------------- */
+
+static void __devinit vt1211_init_device(struct vt1211_data *data)
+{
+ /* set VRM */
+ data->vrm = vid_which_vrm();
+
+ /* Read (and initialize) UCH config */
+ data->uch_config = vt1211_read8(data, VT1211_REG_UCH_CONFIG);
+ if (uch_config > -1) {
+ data->uch_config = (data->uch_config & 0x83) |
+ (uch_config << 2);
+ vt1211_write8(data, VT1211_REG_UCH_CONFIG, data->uch_config);
+ }
+
+ /* Initialize the interrupt mode (if request at module load time).
+ * The VT1211 implements 3 different modes for clearing interrupts:
+ * 0: Clear INT when status register is read. Regenerate INT as long
+ * as temp stays above hysteresis limit.
+ * 1: Clear INT when status register is read. DON'T regenerate INT
+ * until temp falls below hysteresis limit and exceeds hot limit
+ * again.
+ * 2: Clear INT when temp falls below max limit.
+ *
+ * The driver only allows to force mode 0 since that's the only one
+ * that makes sense for 'sensors' */
+ if (int_mode == 0) {
+ vt1211_write8(data, VT1211_REG_TEMP1_CONFIG, 0);
+ vt1211_write8(data, VT1211_REG_TEMP2_CONFIG, 0);
+ }
+
+ /* Fill in some hard wired values into our data struct */
+ data->pwm_auto_pwm[0][3] = 255;
+ data->pwm_auto_pwm[1][3] = 255;
+}
+
+static void vt1211_remove_sysfs(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) {
+ device_remove_file(dev,
+ &vt1211_sysfs_in_input[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_in_min[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_in_max[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_in_alarm[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) {
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_input[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_max[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_max_hyst[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_alarm[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
+ device_remove_file(dev,
+ &vt1211_sysfs_fan_pwm[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) {
+ device_remove_file(dev, &vt1211_sysfs_misc[i]);
+ }
+}
+
+static int __devinit vt1211_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vt1211_data *data;
+ struct resource *res;
+ int i, err;
+
+ if (!(data = kzalloc(sizeof(struct vt1211_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ dev_err(dev, "Out of memory\n");
+ goto EXIT;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ data->addr = res->start;
+ data->name = DRVNAME;
+ mutex_init(&data->update_lock);
+
+ platform_set_drvdata(pdev, data);
+
+ /* Initialize the VT1211 chip */
+ vt1211_init_device(data);
+
+ /* Create sysfs interface files */
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) {
+ if (ISVOLT(i, data->uch_config)) {
+ if ((err = device_create_file(dev,
+ &vt1211_sysfs_in_input[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_in_min[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_in_max[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_in_alarm[i].dev_attr))) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) {
+ if (ISTEMP(i, data->uch_config)) {
+ if ((err = device_create_file(dev,
+ &vt1211_sysfs_temp_input[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_temp_max[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_temp_max_hyst[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_temp_alarm[i].dev_attr))) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
+ err = device_create_file(dev,
+ &vt1211_sysfs_fan_pwm[i].dev_attr);
+ if (err) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) {
+ err = device_create_file(dev,
+ &vt1211_sysfs_misc[i]);
+ if (err) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+
+ /* Register device */
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ dev_err(dev, "Class registration failed (%d)\n", err);
+ goto EXIT_DEV_REMOVE_SILENT;
+ }
+
+ return 0;
+
+EXIT_DEV_REMOVE:
+ dev_err(dev, "Sysfs interface creation failed (%d)\n", err);
+EXIT_DEV_REMOVE_SILENT:
+ vt1211_remove_sysfs(pdev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+EXIT:
+ return err;
+}
+
+static int __devexit vt1211_remove(struct platform_device *pdev)
+{
+ struct vt1211_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->class_dev);
+ vt1211_remove_sysfs(pdev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+
+ return 0;
+}
+
+static struct platform_driver vt1211_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ },
+ .probe = vt1211_probe,
+ .remove = __devexit_p(vt1211_remove),
+};
+
+static int __init vt1211_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + 0x7f,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ printk(KERN_ERR DRVNAME ": Device allocation failed (%d)\n",
+ err);
+ goto EXIT;
+ }
+
+ res.name = pdev->name;
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ printk(KERN_ERR DRVNAME ": Device resource addition failed "
+ "(%d)\n", err);
+ goto EXIT_DEV_PUT;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
+ err);
+ goto EXIT_DEV_PUT;
+ }
+
+ return 0;
+
+EXIT_DEV_PUT:
+ platform_device_put(pdev);
+EXIT:
+ return err;
+}
+
+static int __init vt1211_find(unsigned short *address)
+{
+ int err = -ENODEV;
+
+ superio_enter();
+
+ if (superio_inb(SIO_VT1211_DEVID) != SIO_VT1211_ID) {
+ goto EXIT;
+ }
+
+ superio_select(SIO_VT1211_LDN_HWMON);
+
+ if ((superio_inb(SIO_VT1211_ACTIVE) & 1) == 0) {
+ printk(KERN_WARNING DRVNAME ": HW monitor is disabled, "
+ "skipping\n");
+ goto EXIT;
+ }
+
+ *address = ((superio_inb(SIO_VT1211_BADDR) << 8) |
+ (superio_inb(SIO_VT1211_BADDR + 1))) & 0xff00;
+ if (*address == 0) {
+ printk(KERN_WARNING DRVNAME ": Base address is not set, "
+ "skipping\n");
+ goto EXIT;
+ }
+
+ err = 0;
+ printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, "
+ "revision %u\n", *address, superio_inb(SIO_VT1211_DEVREV));
+
+EXIT:
+ superio_exit();
+ return err;
+}
+
+static int __init vt1211_init(void)
+{
+ int err;
+ unsigned short address = 0;
+
+ err = vt1211_find(&address);
+ if (err) {
+ goto EXIT;
+ }
+
+ if ((uch_config < -1) || (uch_config > 31)) {
+ err = -EINVAL;
+ printk(KERN_WARNING DRVNAME ": Invalid UCH configuration %d. "
+ "Choose a value between 0 and 31.\n", uch_config);
+ goto EXIT;
+ }
+
+ if ((int_mode < -1) || (int_mode > 0)) {
+ err = -EINVAL;
+ printk(KERN_WARNING DRVNAME ": Invalid interrupt mode %d. "
+ "Only mode 0 is supported.\n", int_mode);
+ goto EXIT;
+ }
+
+ err = platform_driver_register(&vt1211_driver);
+ if (err) {
+ goto EXIT;
+ }
+
+ /* Sets global pdev as a side effect */
+ err = vt1211_device_add(address);
+ if (err) {
+ goto EXIT_DRV_UNREGISTER;
+ }
+
+ return 0;
+
+EXIT_DRV_UNREGISTER:
+ platform_driver_unregister(&vt1211_driver);
+EXIT:
+ return err;
+}
+
+static void __exit vt1211_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&vt1211_driver);
+}
+
+MODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>");
+MODULE_DESCRIPTION("VT1211 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(vt1211_init);
+module_exit(vt1211_exit);
diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c
index 686f3deb309..93f93d4fb8a 100644
--- a/drivers/hwmon/vt8231.c
+++ b/drivers/hwmon/vt8231.c
@@ -451,37 +451,6 @@ define_temperature_sysfs(4);
define_temperature_sysfs(5);
define_temperature_sysfs(6);
-#define CFG_INFO_TEMP(id) { &sensor_dev_attr_temp##id##_input.dev_attr, \
- &sensor_dev_attr_temp##id##_max_hyst.dev_attr, \
- &sensor_dev_attr_temp##id##_max.dev_attr }
-#define CFG_INFO_VOLT(id) { &sensor_dev_attr_in##id##_input.dev_attr, \
- &sensor_dev_attr_in##id##_min.dev_attr, \
- &sensor_dev_attr_in##id##_max.dev_attr }
-
-struct str_device_attr_table {
- struct device_attribute *input;
- struct device_attribute *min;
- struct device_attribute *max;
-};
-
-static struct str_device_attr_table cfg_info_temp[] = {
- { &dev_attr_temp1_input, &dev_attr_temp1_max_hyst, &dev_attr_temp1_max },
- CFG_INFO_TEMP(2),
- CFG_INFO_TEMP(3),
- CFG_INFO_TEMP(4),
- CFG_INFO_TEMP(5),
- CFG_INFO_TEMP(6)
-};
-
-static struct str_device_attr_table cfg_info_volt[] = {
- CFG_INFO_VOLT(0),
- CFG_INFO_VOLT(1),
- CFG_INFO_VOLT(2),
- CFG_INFO_VOLT(3),
- CFG_INFO_VOLT(4),
- { &dev_attr_in5_input, &dev_attr_in5_min, &dev_attr_in5_max }
-};
-
/* Fans */
static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -585,8 +554,110 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *vt8231_attributes_temps[6][4] = {
+ {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp1_max.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group vt8231_group_temps[6] = {
+ { .attrs = vt8231_attributes_temps[0] },
+ { .attrs = vt8231_attributes_temps[1] },
+ { .attrs = vt8231_attributes_temps[2] },
+ { .attrs = vt8231_attributes_temps[3] },
+ { .attrs = vt8231_attributes_temps[4] },
+ { .attrs = vt8231_attributes_temps[5] },
+};
+
+static struct attribute *vt8231_attributes_volts[6][4] = {
+ {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ NULL
+ }, {
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group vt8231_group_volts[6] = {
+ { .attrs = vt8231_attributes_volts[0] },
+ { .attrs = vt8231_attributes_volts[1] },
+ { .attrs = vt8231_attributes_volts[2] },
+ { .attrs = vt8231_attributes_volts[3] },
+ { .attrs = vt8231_attributes_volts[4] },
+ { .attrs = vt8231_attributes_volts[5] },
+};
+
+static struct attribute *vt8231_attributes[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group vt8231_group = {
+ .attrs = vt8231_attributes,
+};
+
static struct i2c_driver vt8231_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "vt8231",
},
.attach_adapter = vt8231_detect,
@@ -670,43 +741,43 @@ int vt8231_detect(struct i2c_adapter *adapter)
vt8231_init_client(client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&client->dev.kobj, &vt8231_group)))
goto exit_detach;
- }
/* Must update device information to find out the config field */
data->uch_config = vt8231_read_value(client, VT8231_REG_UCH_CONFIG);
- for (i = 0; i < ARRAY_SIZE(cfg_info_temp); i++) {
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) {
if (ISTEMP(i, data->uch_config)) {
- device_create_file(&client->dev,
- cfg_info_temp[i].input);
- device_create_file(&client->dev, cfg_info_temp[i].max);
- device_create_file(&client->dev, cfg_info_temp[i].min);
+ if ((err = sysfs_create_group(&client->dev.kobj,
+ &vt8231_group_temps[i])))
+ goto exit_remove_files;
}
}
- for (i = 0; i < ARRAY_SIZE(cfg_info_volt); i++) {
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) {
if (ISVOLT(i, data->uch_config)) {
- device_create_file(&client->dev,
- cfg_info_volt[i].input);
- device_create_file(&client->dev, cfg_info_volt[i].max);
- device_create_file(&client->dev, cfg_info_volt[i].min);
+ if ((err = sysfs_create_group(&client->dev.kobj,
+ &vt8231_group_volts[i])))
+ goto exit_remove_files;
}
}
- device_create_file(&client->dev, &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan2_div.dev_attr);
-
- device_create_file(&client->dev, &dev_attr_alarms);
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
+ }
return 0;
+exit_remove_files:
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]);
+
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group);
exit_detach:
i2c_detach_client(client);
exit_free:
@@ -719,10 +790,18 @@ exit_release:
static int vt8231_detach_client(struct i2c_client *client)
{
struct vt8231_data *data = i2c_get_clientdata(client);
- int err;
+ int err, i;
hwmon_device_unregister(data->class_dev);
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]);
+
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group);
+
if ((err = i2c_detach_client(client))) {
return err;
}
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 40301bc6ce1..833faa275ff 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -2,6 +2,9 @@
w83627ehf - Driver for the hardware monitoring functionality of
the Winbond W83627EHF Super-I/O chip
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2006 Yuan Mu (Winbond),
+ Rudolf Marek <r.marek@sh.cvut.cz>
+ David Hubbard <david.c.hubbard@gmail.com>
Shamelessly ripped from the w83627hf driver
Copyright (C) 2003 Mark Studebaker
@@ -29,8 +32,8 @@
Supports the following chips:
- Chip #vin #fan #pwm #temp chip_id man_id
- w83627ehf 10 5 - 3 0x88 0x5ca3
+ Chip #vin #fan #pwm #temp chip_id man_id
+ w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3
*/
#include <linux/module.h>
@@ -145,10 +148,44 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
#define W83627EHF_REG_ALARM2 0x45A
#define W83627EHF_REG_ALARM3 0x45B
+/* SmartFan registers */
+/* DC or PWM output fan configuration */
+static const u8 W83627EHF_REG_PWM_ENABLE[] = {
+ 0x04, /* SYS FAN0 output mode and PWM mode */
+ 0x04, /* CPU FAN0 output mode and PWM mode */
+ 0x12, /* AUX FAN mode */
+ 0x62, /* CPU fan1 mode */
+};
+
+static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
+static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };
+
+/* FAN Duty Cycle, be used to control */
+static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
+static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
+static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
+
+
+/* Advanced Fan control, some values are common for all fans */
+static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
+static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
+
/*
* Conversions
*/
+/* 1 is PWM mode, output in ms */
+static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
+{
+ return mode ? 100 * reg : 400 * reg;
+}
+
+static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
+{
+ return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
+ (msec + 200) / 400), 1, 255);
+}
+
static inline unsigned int
fan_from_reg(u8 reg, unsigned int div)
{
@@ -170,12 +207,12 @@ temp1_from_reg(s8 reg)
}
static inline s8
-temp1_to_reg(int temp)
+temp1_to_reg(int temp, int min, int max)
{
- if (temp <= -128000)
- return -128;
- if (temp >= 127000)
- return 127;
+ if (temp <= min)
+ return min / 1000;
+ if (temp >= max)
+ return max / 1000;
if (temp < 0)
return (temp - 500) / 1000;
return (temp + 500) / 1000;
@@ -223,6 +260,16 @@ struct w83627ehf_data {
s16 temp_max[2];
s16 temp_max_hyst[2];
u32 alarms;
+
+ u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
+ u8 pwm_enable[4]; /* 1->manual
+ 2->thermal cruise (also called SmartFan I) */
+ u8 pwm[4];
+ u8 target_temp[4];
+ u8 tolerance[4];
+
+ u8 fan_min_output[4]; /* minimum fan speed */
+ u8 fan_stop_time[4];
};
static inline int is_word_sized(u16 reg)
@@ -349,6 +396,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83627ehf_data *data = i2c_get_clientdata(client);
+ int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
int i;
mutex_lock(&data->update_lock);
@@ -416,6 +464,34 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
}
}
+ for (i = 0; i < 4; i++) {
+ /* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
+ if (i != 1) {
+ pwmcfg = w83627ehf_read_value(client,
+ W83627EHF_REG_PWM_ENABLE[i]);
+ tolerance = w83627ehf_read_value(client,
+ W83627EHF_REG_TOLERANCE[i]);
+ }
+ data->pwm_mode[i] =
+ ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1)
+ ? 0 : 1;
+ data->pwm_enable[i] =
+ ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
+ & 3) + 1;
+ data->pwm[i] = w83627ehf_read_value(client,
+ W83627EHF_REG_PWM[i]);
+ data->fan_min_output[i] = w83627ehf_read_value(client,
+ W83627EHF_REG_FAN_MIN_OUTPUT[i]);
+ data->fan_stop_time[i] = w83627ehf_read_value(client,
+ W83627EHF_REG_FAN_STOP_TIME[i]);
+ data->target_temp[i] =
+ w83627ehf_read_value(client,
+ W83627EHF_REG_TARGET[i]) &
+ (data->pwm_mode[i] == 1 ? 0x7f : 0xff);
+ data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0))
+ & 0x0f;
+ }
+
/* Measured temperatures and limits */
data->temp1 = w83627ehf_read_value(client,
W83627EHF_REG_TEMP1);
@@ -546,14 +622,6 @@ static struct sensor_device_attribute sda_in_max[] = {
SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
};
-static void device_create_file_in(struct device *dev, int i)
-{
- device_create_file(dev, &sda_in_input[i].dev_attr);
- device_create_file(dev, &sda_in_alarm[i].dev_attr);
- device_create_file(dev, &sda_in_min[i].dev_attr);
- device_create_file(dev, &sda_in_max[i].dev_attr);
-}
-
#define show_fan_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
@@ -681,14 +749,6 @@ static struct sensor_device_attribute sda_fan_div[] = {
SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
};
-static void device_create_file_fan(struct device *dev, int i)
-{
- device_create_file(dev, &sda_fan_input[i].dev_attr);
- device_create_file(dev, &sda_fan_alarm[i].dev_attr);
- device_create_file(dev, &sda_fan_div[i].dev_attr);
- device_create_file(dev, &sda_fan_min[i].dev_attr);
-}
-
#define show_temp1_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
@@ -711,7 +771,7 @@ store_temp1_##reg(struct device *dev, struct device_attribute *attr, \
u32 val = simple_strtoul(buf, NULL, 10); \
\
mutex_lock(&data->update_lock); \
- data->temp1_##reg = temp1_to_reg(val); \
+ data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
data->temp1_##reg); \
mutex_unlock(&data->update_lock); \
@@ -777,10 +837,309 @@ static struct sensor_device_attribute sda_temp[] = {
SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
};
+#define show_pwm_reg(reg) \
+static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", data->reg[nr]); \
+}
+
+show_pwm_reg(pwm_mode)
+show_pwm_reg(pwm_enable)
+show_pwm_reg(pwm)
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u32 val = simple_strtoul(buf, NULL, 10);
+ u16 reg;
+
+ if (val > 1)
+ return -EINVAL;
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
+ data->pwm_mode[nr] = val;
+ reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
+ if (!val)
+ reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
+ w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = val;
+ w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u32 val = simple_strtoul(buf, NULL, 10);
+ u16 reg;
+
+ if (!val || (val > 2)) /* only modes 1 and 2 are supported */
+ return -EINVAL;
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
+ data->pwm_enable[nr] = val;
+ reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
+ reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
+ w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+#define show_tol_temp(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", temp1_from_reg(data->reg[nr])); \
+}
+
+show_tol_temp(tolerance)
+show_tol_temp(target_temp)
+
+static ssize_t
+store_target_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);
+
+ mutex_lock(&data->update_lock);
+ data->target_temp[nr] = val;
+ w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u16 reg;
+ /* Limit the temp to 0C - 15C */
+ u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);
+
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
+ data->tolerance[nr] = val;
+ if (nr == 1)
+ reg = (reg & 0x0f) | (val << 4);
+ else
+ reg = (reg & 0xf0) | val;
+ w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_pwm[] = {
+ SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
+ SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
+ SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
+ SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
+};
+
+static struct sensor_device_attribute sda_pwm_mode[] = {
+ SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 0),
+ SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 1),
+ SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 2),
+ SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 3),
+};
+
+static struct sensor_device_attribute sda_pwm_enable[] = {
+ SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 0),
+ SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 1),
+ SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 2),
+ SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 3),
+};
+
+static struct sensor_device_attribute sda_target_temp[] = {
+ SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 0),
+ SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 1),
+ SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 2),
+ SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 3),
+};
+
+static struct sensor_device_attribute sda_tolerance[] = {
+ SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 0),
+ SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 1),
+ SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 2),
+ SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 3),
+};
+
+/* Smart Fan registers */
+
+#define fan_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", data->reg[nr]); \
+}\
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{\
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83627ehf_data *data = i2c_get_clientdata(client); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
+ mutex_lock(&data->update_lock); \
+ data->reg[nr] = val; \
+ w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+fan_functions(fan_min_output, FAN_MIN_OUTPUT)
+
+#define fan_time_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", \
+ step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
+} \
+\
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83627ehf_data *data = i2c_get_clientdata(client); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
+ data->pwm_mode[nr]); \
+ mutex_lock(&data->update_lock); \
+ data->reg[nr] = val; \
+ w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+} \
+
+fan_time_functions(fan_stop_time, FAN_STOP_TIME)
+
+
+static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
+ SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 3),
+ SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 3),
+};
+
+static struct sensor_device_attribute sda_sf3_arrays[] = {
+ SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 0),
+ SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 1),
+ SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 2),
+ SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 0),
+ SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 1),
+ SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 2),
+};
+
/*
* Driver and client management
*/
+static void w83627ehf_device_remove_files(struct device *dev)
+{
+ /* some entries in the following arrays may not have been used in
+ * device_create_file(), but device_remove_file() will ignore them */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
+ device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
+ device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
+ for (i = 0; i < 10; i++) {
+ device_remove_file(dev, &sda_in_input[i].dev_attr);
+ device_remove_file(dev, &sda_in_alarm[i].dev_attr);
+ device_remove_file(dev, &sda_in_min[i].dev_attr);
+ device_remove_file(dev, &sda_in_max[i].dev_attr);
+ }
+ for (i = 0; i < 5; i++) {
+ device_remove_file(dev, &sda_fan_input[i].dev_attr);
+ device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
+ device_remove_file(dev, &sda_fan_div[i].dev_attr);
+ device_remove_file(dev, &sda_fan_min[i].dev_attr);
+ }
+ for (i = 0; i < 4; i++) {
+ device_remove_file(dev, &sda_pwm[i].dev_attr);
+ device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
+ device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
+ device_remove_file(dev, &sda_target_temp[i].dev_attr);
+ device_remove_file(dev, &sda_tolerance[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
+ device_remove_file(dev, &sda_temp[i].dev_attr);
+}
+
static struct i2c_driver w83627ehf_driver;
static void w83627ehf_init_client(struct i2c_client *client)
@@ -810,6 +1169,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
struct i2c_client *client;
struct w83627ehf_data *data;
struct device *dev;
+ u8 fan4pin, fan5pin;
int i, err = 0;
if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
@@ -848,35 +1208,87 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
data->fan_min[i] = w83627ehf_read_value(client,
W83627EHF_REG_FAN_MIN[i]);
+ /* fan4 and fan5 share some pins with the GPIO and serial flash */
+
+ superio_enter();
+ fan5pin = superio_inb(0x24) & 0x2;
+ fan4pin = superio_inb(0x29) & 0x6;
+ superio_exit();
+
/* It looks like fan4 and fan5 pins can be alternatively used
as fan on/off switches */
+
data->has_fan = 0x07; /* fan1, fan2 and fan3 */
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
- if (i & (1 << 2))
+ if ((i & (1 << 2)) && (!fan4pin))
data->has_fan |= (1 << 3);
- if (i & (1 << 0))
+ if ((i & (1 << 0)) && (!fan5pin))
data->has_fan |= (1 << 4);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- goto exit_detach;
- }
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
+ if ((err = device_create_file(dev,
+ &sda_sf3_arrays[i].dev_attr)))
+ goto exit_remove;
+
+ /* if fan4 is enabled create the sf3 files for it */
+ if (data->has_fan & (1 << 3))
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
+ if ((err = device_create_file(dev,
+ &sda_sf3_arrays_fan4[i].dev_attr)))
+ goto exit_remove;
+ }
for (i = 0; i < 10; i++)
- device_create_file_in(dev, i);
+ if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_in_alarm[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_in_min[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_in_max[i].dev_attr)))
+ goto exit_remove;
for (i = 0; i < 5; i++) {
- if (data->has_fan & (1 << i))
- device_create_file_fan(dev, i);
+ if (data->has_fan & (1 << i)) {
+ if ((err = device_create_file(dev,
+ &sda_fan_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_fan_alarm[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_fan_div[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_fan_min[i].dev_attr)))
+ goto exit_remove;
+ if (i < 4 && /* w83627ehf only has 4 pwm */
+ ((err = device_create_file(dev,
+ &sda_pwm[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_pwm_mode[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_pwm_enable[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_target_temp[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_tolerance[i].dev_attr))))
+ goto exit_remove;
+ }
}
+
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
- device_create_file(dev, &sda_temp[i].dev_attr);
+ if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
+ goto exit_remove;
+
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
+ }
return 0;
-exit_detach:
+exit_remove:
+ w83627ehf_device_remove_files(dev);
i2c_detach_client(client);
exit_free:
kfree(data);
@@ -892,6 +1304,7 @@ static int w83627ehf_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ w83627ehf_device_remove_files(&client->dev);
if ((err = i2c_detach_client(client)))
return err;
@@ -903,6 +1316,7 @@ static int w83627ehf_detach_client(struct i2c_client *client)
static struct i2c_driver w83627ehf_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "w83627ehf",
},
.attach_adapter = w83627ehf_detect,
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index 79368d53c36..dfdc29c7712 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -339,6 +339,7 @@ static void w83627hf_init_client(struct i2c_client *client);
static struct i2c_driver w83627hf_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "w83627hf",
},
.attach_adapter = w83627hf_detect,
@@ -511,13 +512,6 @@ static DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR,
static DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR,
show_regs_in_max0, store_regs_in_max0);
-#define device_create_file_in(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_in##offset##_input); \
-device_create_file(&client->dev, &dev_attr_in##offset##_min); \
-device_create_file(&client->dev, &dev_attr_in##offset##_max); \
-} while (0)
-
#define show_fan_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -575,12 +569,6 @@ sysfs_fan_min_offset(2);
sysfs_fan_offset(3);
sysfs_fan_min_offset(3);
-#define device_create_file_fan(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
-device_create_file(&client->dev, &dev_attr_fan##offset##_min); \
-} while (0)
-
#define show_temp_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -655,13 +643,6 @@ sysfs_temp_offsets(1);
sysfs_temp_offsets(2);
sysfs_temp_offsets(3);
-#define device_create_file_temp(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \
-} while (0)
-
static ssize_t
show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -669,8 +650,6 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid)
static ssize_t
show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -691,8 +670,6 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm)
static ssize_t
show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -701,8 +678,6 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
-#define device_create_file_alarms(client) \
-device_create_file(&client->dev, &dev_attr_alarms)
#define show_beep_reg(REG, reg) \
static ssize_t show_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \
@@ -765,12 +740,6 @@ static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, \
sysfs_beep(ENABLE, enable);
sysfs_beep(MASK, mask);
-#define device_create_file_beep(client) \
-do { \
-device_create_file(&client->dev, &dev_attr_beep_enable); \
-device_create_file(&client->dev, &dev_attr_beep_mask); \
-} while (0)
-
static ssize_t
show_fan_div_reg(struct device *dev, char *buf, int nr)
{
@@ -836,11 +805,6 @@ sysfs_fan_div(1);
sysfs_fan_div(2);
sysfs_fan_div(3);
-#define device_create_file_fan_div(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
-} while (0)
-
static ssize_t
show_pwm_reg(struct device *dev, char *buf, int nr)
{
@@ -895,11 +859,6 @@ sysfs_pwm(1);
sysfs_pwm(2);
sysfs_pwm(3);
-#define device_create_file_pwm(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_pwm##offset); \
-} while (0)
-
static ssize_t
show_sensor_reg(struct device *dev, char *buf, int nr)
{
@@ -971,12 +930,6 @@ sysfs_sensor(1);
sysfs_sensor(2);
sysfs_sensor(3);
-#define device_create_file_sensor(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_type); \
-} while (0)
-
-
static int __init w83627hf_find(int sioaddr, unsigned short *addr)
{
u16 val;
@@ -1008,6 +961,85 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr)
return 0;
}
+static struct attribute *w83627hf_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in7_input.attr,
+ &dev_attr_in7_min.attr,
+ &dev_attr_in7_max.attr,
+ &dev_attr_in8_input.attr,
+ &dev_attr_in8_min.attr,
+ &dev_attr_in8_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp1_type.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ &dev_attr_temp2_type.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+
+ NULL
+};
+
+static const struct attribute_group w83627hf_group = {
+ .attrs = w83627hf_attributes,
+};
+
+static struct attribute *w83627hf_attributes_opt[] = {
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan3_div.attr,
+
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_max_hyst.attr,
+ &dev_attr_temp3_type.attr,
+
+ &dev_attr_pwm3.attr,
+
+ NULL
+};
+
+static const struct attribute_group w83627hf_group_opt = {
+ .attrs = w83627hf_attributes_opt,
+};
+
static int w83627hf_detect(struct i2c_adapter *adapter)
{
int val, kind;
@@ -1107,62 +1139,72 @@ static int w83627hf_detect(struct i2c_adapter *adapter)
data->fan_min[1] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(2));
data->fan_min[2] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(3));
- /* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ /* Register common device attributes */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &w83627hf_group)))
goto ERROR3;
- }
-
- device_create_file_in(new_client, 0);
- if (kind != w83697hf)
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- device_create_file_in(new_client, 4);
- if (kind == w83627hf || kind == w83697hf) {
- device_create_file_in(new_client, 5);
- device_create_file_in(new_client, 6);
- }
- device_create_file_in(new_client, 7);
- device_create_file_in(new_client, 8);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- if (kind != w83697hf)
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- if (kind != w83697hf)
- device_create_file_temp(new_client, 3);
- if (kind != w83697hf && data->vid != 0xff) {
- device_create_file_vid(new_client);
- device_create_file_vrm(new_client);
- }
+ /* Register chip-specific device attributes */
+ if (kind == w83627hf || kind == w83697hf)
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in5_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_max)))
+ goto ERROR4;
- device_create_file_fan_div(new_client, 1);
- device_create_file_fan_div(new_client, 2);
if (kind != w83697hf)
- device_create_file_fan_div(new_client, 3);
-
- device_create_file_alarms(new_client);
-
- device_create_file_beep(new_client);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in1_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in1_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan3_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan3_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan3_div))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_max_hyst))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_type)))
+ goto ERROR4;
+
+ if (kind != w83697hf && data->vid != 0xff)
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_cpu0_vid))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_vrm)))
+ goto ERROR4;
- device_create_file_pwm(new_client, 1);
- device_create_file_pwm(new_client, 2);
if (kind == w83627thf || kind == w83637hf || kind == w83687thf)
- device_create_file_pwm(new_client, 3);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pwm3)))
+ goto ERROR4;
- device_create_file_sensor(new_client, 1);
- device_create_file_sensor(new_client, 2);
- if (kind != w83697hf)
- device_create_file_sensor(new_client, 3);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR4;
+ }
return 0;
+ ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group);
+ sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group_opt);
ERROR3:
i2c_detach_client(new_client);
ERROR2:
@@ -1180,6 +1222,9 @@ static int w83627hf_detach_client(struct i2c_client *client)
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &w83627hf_group);
+ sysfs_remove_group(&client->dev.kobj, &w83627hf_group_opt);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index 7be469ed0f8..a4584ec6984 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -41,6 +41,7 @@
#include <linux/i2c-isa.h>
#include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
+#include <linux/sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <asm/io.h>
@@ -288,6 +289,7 @@ static struct i2c_driver w83781d_driver = {
static struct i2c_driver w83781d_isa_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "w83781d-isa",
},
.attach_adapter = w83781d_isa_attach_adapter,
@@ -359,13 +361,6 @@ sysfs_in_offsets(6);
sysfs_in_offsets(7);
sysfs_in_offsets(8);
-#define device_create_file_in(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_in##offset##_input); \
-device_create_file(&client->dev, &dev_attr_in##offset##_min); \
-device_create_file(&client->dev, &dev_attr_in##offset##_max); \
-} while (0)
-
#define show_fan_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -420,12 +415,6 @@ sysfs_fan_min_offset(2);
sysfs_fan_offset(3);
sysfs_fan_min_offset(3);
-#define device_create_file_fan(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
-device_create_file(&client->dev, &dev_attr_fan##offset##_min); \
-} while (0)
-
#define show_temp_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -496,13 +485,6 @@ sysfs_temp_offsets(1);
sysfs_temp_offsets(2);
sysfs_temp_offsets(3);
-#define device_create_file_temp(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \
-} while (0)
-
static ssize_t
show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -510,10 +492,8 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
-static
-DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid);
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+
static ssize_t
show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -534,10 +514,8 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf
return count;
}
-static
-DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm);
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+
static ssize_t
show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -545,10 +523,8 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%u\n", data->alarms);
}
-static
-DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
-#define device_create_file_alarms(client) \
-device_create_file(&client->dev, &dev_attr_alarms);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+
static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83781d_data *data = w83781d_update_device(dev);
@@ -614,12 +590,6 @@ static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_re
sysfs_beep(ENABLE, enable);
sysfs_beep(MASK, mask);
-#define device_create_file_beep(client) \
-do { \
-device_create_file(&client->dev, &dev_attr_beep_enable); \
-device_create_file(&client->dev, &dev_attr_beep_mask); \
-} while (0)
-
static ssize_t
show_fan_div_reg(struct device *dev, char *buf, int nr)
{
@@ -685,11 +655,6 @@ sysfs_fan_div(1);
sysfs_fan_div(2);
sysfs_fan_div(3);
-#define device_create_file_fan_div(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
-} while (0)
-
static ssize_t
show_pwm_reg(struct device *dev, char *buf, int nr)
{
@@ -786,16 +751,6 @@ sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */
sysfs_pwm(3);
sysfs_pwm(4);
-#define device_create_file_pwm(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_pwm##offset); \
-} while (0)
-
-#define device_create_file_pwmenable(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_pwm##offset##_enable); \
-} while (0)
-
static ssize_t
show_sensor_reg(struct device *dev, char *buf, int nr)
{
@@ -864,11 +819,6 @@ sysfs_sensor(1);
sysfs_sensor(2);
sysfs_sensor(3);
-#define device_create_file_sensor(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_type); \
-} while (0)
-
/* This function is called when:
* w83781d_driver is inserted (when this module is loaded), for each
available adapter
@@ -993,11 +943,69 @@ ERROR_SC_0:
return err;
}
+#define IN_UNIT_ATTRS(X) \
+ &dev_attr_in##X##_input.attr, \
+ &dev_attr_in##X##_min.attr, \
+ &dev_attr_in##X##_max.attr
+
+#define FAN_UNIT_ATTRS(X) \
+ &dev_attr_fan##X##_input.attr, \
+ &dev_attr_fan##X##_min.attr, \
+ &dev_attr_fan##X##_div.attr
+
+#define TEMP_UNIT_ATTRS(X) \
+ &dev_attr_temp##X##_input.attr, \
+ &dev_attr_temp##X##_max.attr, \
+ &dev_attr_temp##X##_max_hyst.attr
+
+static struct attribute* w83781d_attributes[] = {
+ IN_UNIT_ATTRS(0),
+ IN_UNIT_ATTRS(2),
+ IN_UNIT_ATTRS(3),
+ IN_UNIT_ATTRS(4),
+ IN_UNIT_ATTRS(5),
+ IN_UNIT_ATTRS(6),
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2),
+ FAN_UNIT_ATTRS(3),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_mask.attr,
+ &dev_attr_beep_enable.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group = {
+ .attrs = w83781d_attributes,
+};
+
+static struct attribute *w83781d_attributes_opt[] = {
+ IN_UNIT_ATTRS(1),
+ IN_UNIT_ATTRS(7),
+ IN_UNIT_ATTRS(8),
+ TEMP_UNIT_ATTRS(3),
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm2_enable.attr,
+ &dev_attr_pwm3.attr,
+ &dev_attr_pwm4.attr,
+ &dev_attr_temp1_type.attr,
+ &dev_attr_temp2_type.attr,
+ &dev_attr_temp3_type.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group_opt = {
+ .attrs = w83781d_attributes_opt,
+};
+
static int
w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i = 0, val1 = 0, val2;
- struct i2c_client *new_client;
+ struct i2c_client *client;
+ struct device *dev;
struct w83781d_data *data;
int err;
const char *client_name = "";
@@ -1074,13 +1082,14 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
goto ERROR1;
}
- new_client = &data->client;
- i2c_set_clientdata(new_client, data);
- new_client->addr = address;
+ client = &data->client;
+ i2c_set_clientdata(client, data);
+ client->addr = address;
mutex_init(&data->lock);
- new_client->adapter = adapter;
- new_client->driver = is_isa ? &w83781d_isa_driver : &w83781d_driver;
- new_client->flags = 0;
+ client->adapter = adapter;
+ client->driver = is_isa ? &w83781d_isa_driver : &w83781d_driver;
+ client->flags = 0;
+ dev = &client->dev;
/* Now, we do the remaining detection. */
@@ -1089,20 +1098,18 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
force_*=... parameter, and the Winbond will be reset to the right
bank. */
if (kind < 0) {
- if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80) {
- dev_dbg(&new_client->dev, "Detection failed at step "
- "3\n");
+ if (w83781d_read_value(client, W83781D_REG_CONFIG) & 0x80) {
+ dev_dbg(dev, "Detection failed at step 3\n");
err = -ENODEV;
goto ERROR2;
}
- val1 = w83781d_read_value(new_client, W83781D_REG_BANK);
- val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+ val1 = w83781d_read_value(client, W83781D_REG_BANK);
+ val2 = w83781d_read_value(client, W83781D_REG_CHIPMAN);
/* Check for Winbond or Asus ID if in bank 0 */
if ((!(val1 & 0x07)) &&
(((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
|| ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) {
- dev_dbg(&new_client->dev, "Detection failed at step "
- "4\n");
+ dev_dbg(dev, "Detection failed at step 4\n");
err = -ENODEV;
goto ERROR2;
}
@@ -1111,9 +1118,8 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) ||
((val1 & 0x80) && (val2 == 0x5c)))) {
if (w83781d_read_value
- (new_client, W83781D_REG_I2C_ADDR) != address) {
- dev_dbg(&new_client->dev, "Detection failed "
- "at step 5\n");
+ (client, W83781D_REG_I2C_ADDR) != address) {
+ dev_dbg(dev, "Detection failed at step 5\n");
err = -ENODEV;
goto ERROR2;
}
@@ -1122,27 +1128,26 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
/* We have either had a force parameter, or we have already detected the
Winbond. Put it now into bank 0 and Vendor ID High Byte */
- w83781d_write_value(new_client, W83781D_REG_BANK,
- (w83781d_read_value(new_client,
- W83781D_REG_BANK) & 0x78) |
- 0x80);
+ w83781d_write_value(client, W83781D_REG_BANK,
+ (w83781d_read_value(client, W83781D_REG_BANK)
+ & 0x78) | 0x80);
/* Determine the chip type. */
if (kind <= 0) {
/* get vendor ID */
- val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+ val2 = w83781d_read_value(client, W83781D_REG_CHIPMAN);
if (val2 == 0x5c)
vendid = winbond;
else if (val2 == 0x12)
vendid = asus;
else {
- dev_dbg(&new_client->dev, "Chip was made by neither "
+ dev_dbg(dev, "Chip was made by neither "
"Winbond nor Asus?\n");
err = -ENODEV;
goto ERROR2;
}
- val1 = w83781d_read_value(new_client, W83781D_REG_WCHIPID);
+ val1 = w83781d_read_value(client, W83781D_REG_WCHIPID);
if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
kind = w83781d;
else if (val1 == 0x30 && vendid == winbond)
@@ -1156,7 +1161,7 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
kind = as99127f;
else {
if (kind == 0)
- dev_warn(&new_client->dev, "Ignoring 'force' "
+ dev_warn(dev, "Ignoring 'force' "
"parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
@@ -1178,20 +1183,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Fill in the remaining client fields and put into the global list */
- strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
+ strlcpy(client->name, client_name, I2C_NAME_SIZE);
data->type = kind;
data->valid = 0;
mutex_init(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
- if ((err = i2c_attach_client(new_client)))
+ if ((err = i2c_attach_client(client)))
goto ERROR2;
/* attach secondary i2c lm75-like clients */
if (!is_isa) {
if ((err = w83781d_detect_subclients(adapter, address,
- kind, new_client)))
+ kind, client)))
goto ERROR3;
} else {
data->lm75[0] = NULL;
@@ -1199,11 +1204,11 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Initialize the chip */
- w83781d_init_client(new_client);
+ w83781d_init_client(client);
/* A few vars need to be filled upon startup */
for (i = 1; i <= 3; i++) {
- data->fan_min[i - 1] = w83781d_read_value(new_client,
+ data->fan_min[i - 1] = w83781d_read_value(client,
W83781D_REG_FAN_MIN(i));
}
if (kind != w83781d && kind != as99127f)
@@ -1211,65 +1216,68 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
data->pwmenable[i] = 1;
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&dev->kobj, &w83781d_group)))
goto ERROR4;
- }
- device_create_file_in(new_client, 0);
- if (kind != w83783s)
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- device_create_file_in(new_client, 4);
- device_create_file_in(new_client, 5);
- device_create_file_in(new_client, 6);
+ if (kind != w83783s) {
+ if ((err = device_create_file(dev, &dev_attr_in1_input))
+ || (err = device_create_file(dev, &dev_attr_in1_min))
+ || (err = device_create_file(dev, &dev_attr_in1_max)))
+ goto ERROR4;
+ }
if (kind != as99127f && kind != w83781d && kind != w83783s) {
- device_create_file_in(new_client, 7);
- device_create_file_in(new_client, 8);
+ if ((err = device_create_file(dev, &dev_attr_in7_input))
+ || (err = device_create_file(dev, &dev_attr_in7_min))
+ || (err = device_create_file(dev, &dev_attr_in7_max))
+ || (err = device_create_file(dev, &dev_attr_in8_input))
+ || (err = device_create_file(dev, &dev_attr_in8_min))
+ || (err = device_create_file(dev, &dev_attr_in8_max)))
+ goto ERROR4;
+ }
+ if (kind != w83783s) {
+ if ((err = device_create_file(dev, &dev_attr_temp3_input))
+ || (err = device_create_file(dev, &dev_attr_temp3_max))
+ || (err = device_create_file(dev,
+ &dev_attr_temp3_max_hyst)))
+ goto ERROR4;
}
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- if (kind != w83783s)
- device_create_file_temp(new_client, 3);
-
- device_create_file_vid(new_client);
- device_create_file_vrm(new_client);
-
- device_create_file_fan_div(new_client, 1);
- device_create_file_fan_div(new_client, 2);
- device_create_file_fan_div(new_client, 3);
-
- device_create_file_alarms(new_client);
-
- device_create_file_beep(new_client);
if (kind != w83781d && kind != as99127f) {
- device_create_file_pwm(new_client, 1);
- device_create_file_pwm(new_client, 2);
- device_create_file_pwmenable(new_client, 2);
+ if ((err = device_create_file(dev, &dev_attr_pwm1))
+ || (err = device_create_file(dev, &dev_attr_pwm2))
+ || (err = device_create_file(dev, &dev_attr_pwm2_enable)))
+ goto ERROR4;
}
if (kind == w83782d && !is_isa) {
- device_create_file_pwm(new_client, 3);
- device_create_file_pwm(new_client, 4);
+ if ((err = device_create_file(dev, &dev_attr_pwm3))
+ || (err = device_create_file(dev, &dev_attr_pwm4)))
+ goto ERROR4;
}
if (kind != as99127f && kind != w83781d) {
- device_create_file_sensor(new_client, 1);
- device_create_file_sensor(new_client, 2);
- if (kind != w83783s)
- device_create_file_sensor(new_client, 3);
+ if ((err = device_create_file(dev, &dev_attr_temp1_type))
+ || (err = device_create_file(dev,
+ &dev_attr_temp2_type)))
+ goto ERROR4;
+ if (kind != w83783s) {
+ if ((err = device_create_file(dev,
+ &dev_attr_temp3_type)))
+ goto ERROR4;
+ }
+ }
+
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR4;
}
return 0;
ERROR4:
+ sysfs_remove_group(&dev->kobj, &w83781d_group);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_opt);
+
if (data->lm75[1]) {
i2c_detach_client(data->lm75[1]);
kfree(data->lm75[1]);
@@ -1279,7 +1287,7 @@ ERROR4:
kfree(data->lm75[0]);
}
ERROR3:
- i2c_detach_client(new_client);
+ i2c_detach_client(client);
ERROR2:
kfree(data);
ERROR1:
@@ -1296,9 +1304,11 @@ w83781d_detach_client(struct i2c_client *client)
int err;
/* main client */
- if (data)
+ if (data) {
hwmon_device_unregister(data->class_dev);
-
+ sysfs_remove_group(&client->dev.kobj, &w83781d_group);
+ sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt);
+ }
if (i2c_is_isa_client(client))
release_region(client->addr, W83781D_EXTENT);
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index eec43abd57f..d965d074cd6 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -27,9 +27,9 @@
The w83791d chip appears to be part way between the 83781d and the
83792d. Thus, this file is derived from both the w83792d.c and
- w83781d.c files, but its output is more along the lines of the
- 83781d (which means there are no changes to the user-mode sensors
- program which treats the 83791d as an 83781d).
+ w83781d.c files.
+
+ The w83791g chip is the same as the w83791d but lead-free.
*/
#include <linux/config.h>
@@ -1172,6 +1172,7 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
(w83791d_read(client, W83791D_REG_BEEP_CTRL[1]) << 8) +
(w83791d_read(client, W83791D_REG_BEEP_CTRL[2]) << 16);
+ /* Extract global beep enable flag */
data->beep_enable =
(data->beep_mask >> GLOBAL_BEEP_ENABLE_SHIFT) & 0x01;
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 7576ec9426a..4e108262576 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -43,6 +43,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
@@ -381,41 +382,6 @@ static ssize_t store_in_##reg (struct device *dev, \
store_in_reg(MIN, min);
store_in_reg(MAX, max);
-static struct sensor_device_attribute sda_in_input[] = {
- SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
- SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
- SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
- SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
- SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
- SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
- SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
- SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
- SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
-};
-static struct sensor_device_attribute sda_in_min[] = {
- SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
- SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
- SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
- SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
- SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
- SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
- SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
- SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
- SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
-};
-static struct sensor_device_attribute sda_in_max[] = {
- SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
- SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
- SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
- SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
- SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
- SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
- SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
- SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
- SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
-};
-
-
#define show_fan_reg(reg) \
static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \
char *buf) \
@@ -499,35 +465,6 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_fan_input[] = {
- SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 1),
- SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 2),
- SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 3),
- SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 4),
- SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 5),
- SENSOR_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 6),
- SENSOR_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 7),
-};
-static struct sensor_device_attribute sda_fan_min[] = {
- SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 1),
- SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 2),
- SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 3),
- SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 4),
- SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 5),
- SENSOR_ATTR(fan6_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 6),
- SENSOR_ATTR(fan7_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 7),
-};
-static struct sensor_device_attribute sda_fan_div[] = {
- SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 1),
- SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 2),
- SENSOR_ATTR(fan3_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 3),
- SENSOR_ATTR(fan4_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 4),
- SENSOR_ATTR(fan5_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 5),
- SENSOR_ATTR(fan6_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 6),
- SENSOR_ATTR(fan7_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 7),
-};
-
-
/* read/write the temperature1, includes measured value and limits */
static ssize_t show_temp1(struct device *dev, struct device_attribute *attr,
@@ -595,24 +532,6 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute_2 sda_temp_input[] = {
- SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0),
- SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0),
- SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0),
-};
-
-static struct sensor_device_attribute_2 sda_temp_max[] = {
- SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp1, store_temp1, 0, 1),
- SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 0, 2),
- SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 1, 2),
-};
-
-static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
- SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1, store_temp1, 0, 2),
- SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 0, 4),
- SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 1, 4),
-};
-
/* get reatime status of all sensors items: voltage, temp, fan */
static ssize_t
show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -621,9 +540,6 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%d\n", data->alarms);
}
-static
-DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
-
static ssize_t
show_pwm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -715,21 +631,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_pwm[] = {
- SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
- SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
- SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
-};
-static struct sensor_device_attribute sda_pwm_enable[] = {
- SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
- show_pwmenable, store_pwmenable, 1),
- SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
- show_pwmenable, store_pwmenable, 2),
- SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
- show_pwmenable, store_pwmenable, 3),
-};
-
-
static ssize_t
show_pwm_mode(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -767,16 +668,6 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_pwm_mode[] = {
- SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
- show_pwm_mode, store_pwm_mode, 0),
- SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
- show_pwm_mode, store_pwm_mode, 1),
- SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
- show_pwm_mode, store_pwm_mode, 2),
-};
-
-
static ssize_t
show_regs_chassis(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -785,8 +676,6 @@ show_regs_chassis(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%d\n", data->chassis);
}
-static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL);
-
static ssize_t
show_chassis_clear(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -815,9 +704,6 @@ store_chassis_clear(struct device *dev, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR,
- show_chassis_clear, store_chassis_clear);
-
/* For Smart Fan I / Thermal Cruise */
static ssize_t
show_thermal_cruise(struct device *dev, struct device_attribute *attr,
@@ -853,15 +739,6 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_thermal_cruise[] = {
- SENSOR_ATTR(thermal_cruise1, S_IWUSR | S_IRUGO,
- show_thermal_cruise, store_thermal_cruise, 1),
- SENSOR_ATTR(thermal_cruise2, S_IWUSR | S_IRUGO,
- show_thermal_cruise, store_thermal_cruise, 2),
- SENSOR_ATTR(thermal_cruise3, S_IWUSR | S_IRUGO,
- show_thermal_cruise, store_thermal_cruise, 3),
-};
-
/* For Smart Fan I/Thermal Cruise and Smart Fan II */
static ssize_t
show_tolerance(struct device *dev, struct device_attribute *attr,
@@ -901,15 +778,6 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_tolerance[] = {
- SENSOR_ATTR(tolerance1, S_IWUSR | S_IRUGO,
- show_tolerance, store_tolerance, 1),
- SENSOR_ATTR(tolerance2, S_IWUSR | S_IRUGO,
- show_tolerance, store_tolerance, 2),
- SENSOR_ATTR(tolerance3, S_IWUSR | S_IRUGO,
- show_tolerance, store_tolerance, 3),
-};
-
/* For Smart Fan II */
static ssize_t
show_sf2_point(struct device *dev, struct device_attribute *attr,
@@ -946,36 +814,6 @@ store_sf2_point(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute_2 sda_sf2_point[] = {
- SENSOR_ATTR_2(sf2_point1_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 1, 1),
- SENSOR_ATTR_2(sf2_point2_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 2, 1),
- SENSOR_ATTR_2(sf2_point3_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 3, 1),
- SENSOR_ATTR_2(sf2_point4_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 4, 1),
-
- SENSOR_ATTR_2(sf2_point1_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 1, 2),
- SENSOR_ATTR_2(sf2_point2_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 2, 2),
- SENSOR_ATTR_2(sf2_point3_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 3, 2),
- SENSOR_ATTR_2(sf2_point4_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 4, 2),
-
- SENSOR_ATTR_2(sf2_point1_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 1, 3),
- SENSOR_ATTR_2(sf2_point2_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 2, 3),
- SENSOR_ATTR_2(sf2_point3_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 3, 3),
- SENSOR_ATTR_2(sf2_point4_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 4, 3),
-};
-
-
static ssize_t
show_sf2_level(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1016,29 +854,6 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute_2 sda_sf2_level[] = {
- SENSOR_ATTR_2(sf2_level1_fan1, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 1, 1),
- SENSOR_ATTR_2(sf2_level2_fan1, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 2, 1),
- SENSOR_ATTR_2(sf2_level3_fan1, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 3, 1),
-
- SENSOR_ATTR_2(sf2_level1_fan2, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 1, 2),
- SENSOR_ATTR_2(sf2_level2_fan2, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 2, 2),
- SENSOR_ATTR_2(sf2_level3_fan2, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 3, 2),
-
- SENSOR_ATTR_2(sf2_level1_fan3, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 1, 3),
- SENSOR_ATTR_2(sf2_level2_fan3, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 2, 3),
- SENSOR_ATTR_2(sf2_level3_fan3, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 3, 3),
-};
-
/* This function is called when:
* w83792d_driver is inserted (when this module is loaded), for each
available adapter
@@ -1139,12 +954,297 @@ ERROR_SC_0:
return err;
}
-static void device_create_file_fan(struct device *dev, int i)
-{
- device_create_file(dev, &sda_fan_input[i].dev_attr);
- device_create_file(dev, &sda_fan_div[i].dev_attr);
- device_create_file(dev, &sda_fan_min[i].dev_attr);
-}
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 0);
+static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 3);
+static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 4);
+static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 5);
+static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 6);
+static SENSOR_DEVICE_ATTR(in7_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 7);
+static SENSOR_DEVICE_ATTR(in8_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 8);
+static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 0);
+static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 1);
+static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 2);
+static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 3);
+static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 4);
+static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 5);
+static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 6);
+static SENSOR_DEVICE_ATTR(in7_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 7);
+static SENSOR_DEVICE_ATTR(in8_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 8);
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp23,
+ store_temp23, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp23,
+ store_temp23, 1, 2);
+static SENSOR_DEVICE_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 0, 4);
+static SENSOR_DEVICE_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 1, 4);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL);
+static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR,
+ show_chassis_clear, store_chassis_clear);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 1);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 2);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 3);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 1);
+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 2);
+static SENSOR_DEVICE_ATTR(tolerance1, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 1);
+static SENSOR_DEVICE_ATTR(tolerance2, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 2);
+static SENSOR_DEVICE_ATTR(tolerance3, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 3);
+static SENSOR_DEVICE_ATTR(thermal_cruise1, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 1);
+static SENSOR_DEVICE_ATTR(thermal_cruise2, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 2);
+static SENSOR_DEVICE_ATTR(thermal_cruise3, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 3);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 5);
+static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 2);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 3);
+static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 4);
+static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 5);
+static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 6);
+static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 7);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 1);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 2);
+static SENSOR_DEVICE_ATTR(fan3_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 3);
+static SENSOR_DEVICE_ATTR(fan4_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 4);
+static SENSOR_DEVICE_ATTR(fan5_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 5);
+static SENSOR_DEVICE_ATTR(fan6_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 6);
+static SENSOR_DEVICE_ATTR(fan7_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 7);
+
+static struct attribute *w83792d_attributes_fan[4][4] = {
+ {
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_div.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_div.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_div.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group w83792d_group_fan[4] = {
+ { .attrs = w83792d_attributes_fan[0] },
+ { .attrs = w83792d_attributes_fan[1] },
+ { .attrs = w83792d_attributes_fan[2] },
+ { .attrs = w83792d_attributes_fan[3] },
+};
+
+static struct attribute *w83792d_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_chassis.attr,
+ &dev_attr_chassis_clear.attr,
+ &sensor_dev_attr_tolerance1.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise1.dev_attr.attr,
+ &sensor_dev_attr_tolerance2.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise2.dev_attr.attr,
+ &sensor_dev_attr_tolerance3.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan3.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group w83792d_group = {
+ .attrs = w83792d_attributes,
+};
static int
w83792d_detect(struct i2c_adapter *adapter, int address, int kind)
@@ -1268,59 +1368,46 @@ w83792d_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&dev->kobj, &w83792d_group)))
goto ERROR3;
- }
- for (i = 0; i < 9; i++) {
- device_create_file(dev, &sda_in_input[i].dev_attr);
- device_create_file(dev, &sda_in_max[i].dev_attr);
- device_create_file(dev, &sda_in_min[i].dev_attr);
- }
- for (i = 0; i < 3; i++)
- device_create_file_fan(dev, i);
/* Read GPIO enable register to check if pins for fan 4,5 are used as
GPIO */
val1 = w83792d_read_value(client, W83792D_REG_GPIO_EN);
+
if (!(val1 & 0x40))
- device_create_file_fan(dev, 3);
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[0])))
+ goto exit_remove_files;
+
if (!(val1 & 0x20))
- device_create_file_fan(dev, 4);
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[1])))
+ goto exit_remove_files;
val1 = w83792d_read_value(client, W83792D_REG_PIN);
if (val1 & 0x40)
- device_create_file_fan(dev, 5);
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[2])))
+ goto exit_remove_files;
+
if (val1 & 0x04)
- device_create_file_fan(dev, 6);
-
- for (i = 0; i < 3; i++) {
- device_create_file(dev, &sda_temp_input[i].dev_attr);
- device_create_file(dev, &sda_temp_max[i].dev_attr);
- device_create_file(dev, &sda_temp_max_hyst[i].dev_attr);
- device_create_file(dev, &sda_thermal_cruise[i].dev_attr);
- device_create_file(dev, &sda_tolerance[i].dev_attr);
- }
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[3])))
+ goto exit_remove_files;
- for (i = 0; i < ARRAY_SIZE(sda_pwm); i++) {
- device_create_file(dev, &sda_pwm[i].dev_attr);
- device_create_file(dev, &sda_pwm_enable[i].dev_attr);
- device_create_file(dev, &sda_pwm_mode[i].dev_attr);
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
- device_create_file(dev, &dev_attr_alarms);
- device_create_file(dev, &dev_attr_chassis);
- device_create_file(dev, &dev_attr_chassis_clear);
-
- for (i = 0; i < ARRAY_SIZE(sda_sf2_point); i++)
- device_create_file(dev, &sda_sf2_point[i].dev_attr);
-
- for (i = 0; i < ARRAY_SIZE(sda_sf2_level); i++)
- device_create_file(dev, &sda_sf2_level[i].dev_attr);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&dev->kobj, &w83792d_group);
+ for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++)
+ sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]);
ERROR3:
if (data->lm75[0] != NULL) {
i2c_detach_client(data->lm75[0]);
@@ -1342,11 +1429,16 @@ static int
w83792d_detach_client(struct i2c_client *client)
{
struct w83792d_data *data = i2c_get_clientdata(client);
- int err;
+ int err, i;
/* main client */
- if (data)
+ if (data) {
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &w83792d_group);
+ for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++)
+ sysfs_remove_group(&client->dev.kobj,
+ &w83792d_group_fan[i]);
+ }
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
index 3f2bac125fb..a3fcace412f 100644
--- a/drivers/hwmon/w83l785ts.c
+++ b/drivers/hwmon/w83l785ts.c
@@ -236,21 +236,30 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
* Nothing yet, assume it is already started.
*/
+ err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_temp1_max.dev_attr);
+ if (err)
+ goto exit_remove;
+
/* Register sysfs hooks */
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
-
return 0;
-exit_detach:
+exit_remove:
+ device_remove_file(&new_client->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&new_client->dev,
+ &sensor_dev_attr_temp1_max.dev_attr);
i2c_detach_client(new_client);
exit_free:
kfree(data);
@@ -264,7 +273,10 @@ static int w83l785ts_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
-
+ device_remove_file(&client->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&client->dev,
+ &sensor_dev_attr_temp1_max.dev_attr);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 24383afdda7..11935f66fcd 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -1,5 +1,5 @@
#
-# Character device configuration
+# I2C subsystem configuration
#
menu "I2C support"
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index 30408015d23..c034820615b 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -53,12 +53,6 @@ config I2C_ALGO8XX
tristate "MPC8xx CPM I2C interface"
depends on 8xx && I2C
-config I2C_ALGO_SIBYTE
- tristate "SiByte SMBus interface"
- depends on SIBYTE_SB1xxx_SOC && I2C
- help
- Supports the SiByte SOC on-chip I2C interfaces (2 channels).
-
config I2C_ALGO_SGI
tristate "I2C SGI interfaces"
depends on I2C && (SGI_IP22 || SGI_IP32 || X86_VISWS)
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index 867fe1f6740..208be04a3db 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -6,7 +6,6 @@ obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o
obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o
obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o
-obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o
obj-$(CONFIG_I2C_ALGO_SGI) += i2c-algo-sgi.o
ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index ab230c033f9..21c36bfb5e6 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -76,17 +76,15 @@ static inline void scllo(struct i2c_algo_bit_data *adap)
* Raise scl line, and do checking for delays. This is necessary for slower
* devices.
*/
-static inline int sclhi(struct i2c_algo_bit_data *adap)
+static int sclhi(struct i2c_algo_bit_data *adap)
{
unsigned long start;
setscl(adap,1);
/* Not all adapters have scl sense line... */
- if (adap->getscl == NULL ) {
- udelay(adap->udelay);
- return 0;
- }
+ if (!adap->getscl)
+ goto done;
start=jiffies;
while (! getscl(adap) ) {
@@ -101,6 +99,8 @@ static inline int sclhi(struct i2c_algo_bit_data *adap)
cond_resched();
}
DEBSTAT(printk(KERN_DEBUG "needed %ld jiffies\n", jiffies-start));
+
+done:
udelay(adap->udelay);
return 0;
}
@@ -121,7 +121,6 @@ static void i2c_repstart(struct i2c_algo_bit_data *adap)
DEBPROTO(printk(" Sr "));
setsda(adap,1);
sclhi(adap);
- udelay(adap->udelay);
sdalo(adap);
scllo(adap);
@@ -306,7 +305,7 @@ bailout:
* 0 chip did not answer
* -x transmission error
*/
-static inline int try_address(struct i2c_adapter *i2c_adap,
+static int try_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
@@ -354,15 +353,11 @@ static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
return (retval<0)? retval : -EFAULT;
/* got a better one ?? */
}
-#if 0
- /* from asm/delay.h */
- __delay(adap->mdelay * (loops_per_sec / 1000) );
-#endif
}
return wrcount;
}
-static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
+static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
int inval;
int rdcount=0; /* counts bytes read */
@@ -412,7 +407,7 @@ static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
* -x an error occurred (like: -EREMOTEIO if the device did not answer, or
* -ETIMEDOUT, for example if the lines are stuck...)
*/
-static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
+static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
unsigned short flags = msg->flags;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
@@ -517,7 +512,7 @@ static u32 bit_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
-static struct i2c_algorithm i2c_bit_algo = {
+static const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index b88a6fcf7bd..9081c9fbcd2 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -355,7 +355,7 @@ static int pca_init(struct i2c_algo_pca_data *adap)
return 0;
}
-static struct i2c_algorithm pca_algo = {
+static const struct i2c_algorithm pca_algo = {
.master_xfer = pca_xfer,
.functionality = pca_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c
index 5b24930adb5..3b200339896 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.c
+++ b/drivers/i2c/algos/i2c-algo-pcf.c
@@ -458,7 +458,7 @@ static u32 pcf_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
-static struct i2c_algorithm pcf_algo = {
+static const struct i2c_algorithm pcf_algo = {
.master_xfer = pcf_xfer,
.functionality = pcf_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-sgi.c b/drivers/i2c/algos/i2c-algo-sgi.c
index 932c4fa86c7..490d99997fd 100644
--- a/drivers/i2c/algos/i2c-algo-sgi.c
+++ b/drivers/i2c/algos/i2c-algo-sgi.c
@@ -157,7 +157,7 @@ static u32 sgi_func(struct i2c_adapter *adap)
return I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm sgi_algo = {
+static const struct i2c_algorithm sgi_algo = {
.master_xfer = sgi_xfer,
.functionality = sgi_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-sibyte.c b/drivers/i2c/algos/i2c-algo-sibyte.c
deleted file mode 100644
index 32d41c6fac0..00000000000
--- a/drivers/i2c/algos/i2c-algo-sibyte.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/* ------------------------------------------------------------------------- */
-/* i2c-algo-sibyte.c i2c driver algorithms for bit-shift adapters */
-/* ------------------------------------------------------------------------- */
-/* Copyright (C) 2001,2002,2003 Broadcom Corporation
- Copyright (C) 1995-2000 Simon G. Vogl
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-/* ------------------------------------------------------------------------- */
-
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
- Frodo Looijaard <frodol@dds.nl>. */
-
-/* Ported for SiByte SOCs by Broadcom Corporation. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <asm/io.h>
-#include <asm/sibyte/sb1250_regs.h>
-#include <asm/sibyte/sb1250_smbus.h>
-
-#include <linux/i2c.h>
-#include <linux/i2c-algo-sibyte.h>
-
-/* ----- global defines ----------------------------------------------- */
-#define SMB_CSR(a,r) ((long)(a->reg_base + r))
-
-/* ----- global variables --------------------------------------------- */
-
-/* module parameters:
- */
-static int bit_scan; /* have a look at what's hanging 'round */
-
-
-static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data * data)
-{
- struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
- int data_bytes = 0;
- int error;
-
- while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
- ;
-
- switch (size) {
- case I2C_SMBUS_QUICK:
- csr_out32((V_SMB_ADDR(addr) | (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
- V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
- break;
- case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_READ) {
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
- SMB_CSR(adap, R_SMB_START));
- data_bytes = 1;
- } else {
- csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
- SMB_CSR(adap, R_SMB_START));
- }
- break;
- case I2C_SMBUS_BYTE_DATA:
- csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
- if (read_write == I2C_SMBUS_READ) {
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
- SMB_CSR(adap, R_SMB_START));
- data_bytes = 1;
- } else {
- csr_out32(V_SMB_LB(data->byte), SMB_CSR(adap, R_SMB_DATA));
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
- SMB_CSR(adap, R_SMB_START));
- }
- break;
- case I2C_SMBUS_WORD_DATA:
- csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
- if (read_write == I2C_SMBUS_READ) {
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
- SMB_CSR(adap, R_SMB_START));
- data_bytes = 2;
- } else {
- csr_out32(V_SMB_LB(data->word & 0xff), SMB_CSR(adap, R_SMB_DATA));
- csr_out32(V_SMB_MB(data->word >> 8), SMB_CSR(adap, R_SMB_DATA));
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
- SMB_CSR(adap, R_SMB_START));
- }
- break;
- default:
- return -1; /* XXXKW better error code? */
- }
-
- while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
- ;
-
- error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
- if (error & M_SMB_ERROR) {
- /* Clear error bit by writing a 1 */
- csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
- return -1; /* XXXKW better error code? */
- }
-
- if (data_bytes == 1)
- data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
- if (data_bytes == 2)
- data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
-
- return 0;
-}
-
-static int algo_control(struct i2c_adapter *adapter,
- unsigned int cmd, unsigned long arg)
-{
- return 0;
-}
-
-static u32 bit_func(struct i2c_adapter *adap)
-{
- return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
-}
-
-
-/* -----exported algorithm data: ------------------------------------- */
-
-static struct i2c_algorithm i2c_sibyte_algo = {
- .smbus_xfer = smbus_xfer,
- .algo_control = algo_control, /* ioctl */
- .functionality = bit_func,
-};
-
-/*
- * registering functions to load algorithms at runtime
- */
-int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
-{
- int i;
- struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
-
- /* register new adapter to i2c module... */
- i2c_adap->algo = &i2c_sibyte_algo;
-
- /* Set the frequency to 100 kHz */
- csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
- csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
-
- /* scan bus */
- if (bit_scan) {
- union i2c_smbus_data data;
- int rc;
- printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n",
- i2c_adap->name);
- for (i = 0x00; i < 0x7f; i++) {
- /* XXXKW is this a realistic probe? */
- rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0,
- I2C_SMBUS_BYTE_DATA, &data);
- if (!rc) {
- printk("(%02x)",i);
- } else
- printk(".");
- }
- printk("\n");
- }
-
- return i2c_add_adapter(i2c_adap);
-}
-
-
-int i2c_sibyte_del_bus(struct i2c_adapter *adap)
-{
- int res;
-
- if ((res = i2c_del_adapter(adap)) < 0)
- return res;
-
- return 0;
-}
-
-int __init i2c_algo_sibyte_init (void)
-{
- printk("i2c-algo-sibyte.o: i2c SiByte algorithm module\n");
- return 0;
-}
-
-
-EXPORT_SYMBOL(i2c_sibyte_add_bus);
-EXPORT_SYMBOL(i2c_sibyte_del_bus);
-
-#ifdef MODULE
-MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
-MODULE_DESCRIPTION("SiByte I2C-Bus algorithm");
-module_param(bit_scan, int, 0);
-MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus");
-MODULE_LICENSE("GPL");
-
-int init_module(void)
-{
- return i2c_algo_sibyte_init();
-}
-
-void cleanup_module(void)
-{
-}
-#endif
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 884320e7040..0d9667921f6 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -75,11 +75,11 @@ config I2C_AMD8111
will be called i2c-amd8111.
config I2C_AU1550
- tristate "Au1550 SMBus interface"
- depends on I2C && SOC_AU1550
+ tristate "Au1550/Au1200 SMBus interface"
+ depends on I2C && (SOC_AU1550 || SOC_AU1200)
help
If you say yes to this option, support will be included for the
- Au1550 SMBus interface.
+ Au1550 and Au1200 SMBus interface.
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
@@ -196,7 +196,7 @@ config I2C_IBM_IIC
config I2C_IOP3XX
tristate "Intel IOP3xx and IXP4xx on-chip I2C interface"
- depends on (ARCH_IOP3XX || ARCH_IXP4XX) && I2C
+ depends on (ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX) && I2C
help
Say Y here if you want to use the IIC bus controller on
the Intel IOP3xx I/O Processors or IXP4xx Network Processors.
@@ -287,6 +287,16 @@ config I2C_OCORES
This driver can also be built as a module. If so, the module
will be called i2c-ocores.
+config I2C_OMAP
+ tristate "OMAP I2C adapter"
+ depends on I2C && ARCH_OMAP
+ default y if MACH_OMAP_H3 || MACH_OMAP_OSK
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Texas Instruments OMAP1/2 family of processors.
+ Like OMAP1510/1610/1710/5912 and OMAP242x.
+ For details see http://www.ti.com/omap.
+
config I2C_PARPORT
tristate "Parallel port adapter"
depends on I2C && PARPORT
@@ -482,19 +492,19 @@ config I2C_VIA
will be called i2c-via.
config I2C_VIAPRO
- tristate "VIA 82C596/82C686/823x"
+ tristate "VIA 82C596/82C686/82xx"
depends on I2C && PCI
help
If you say yes to this option, support will be included for the VIA
- 82C596/82C686/823x I2C interfaces. Specifically, the following
+ 82C596/82C686/82xx I2C interfaces. Specifically, the following
chipsets are supported:
- 82C596A/B
- 82C686A/B
- 8231
- 8233
- 8233A
- 8235
- 8237
+ VT82C596A/B
+ VT82C686A/B
+ VT8231
+ VT8233/A
+ VT8235
+ VT8237R/A
+ VT8251
This driver can also be built as a module. If so, the module
will be called i2c-viapro.
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ac56df53155..493c87289b6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
+obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index d3ef46aeeb3..e75d339a348 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -468,7 +468,7 @@ static u32 ali1535_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = ali1535_access,
.functionality = ali1535_func,
};
diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c
index e6f63208fc4..33fbb47100a 100644
--- a/drivers/i2c/busses/i2c-ali1563.c
+++ b/drivers/i2c/busses/i2c-ali1563.c
@@ -367,7 +367,7 @@ static void ali1563_shutdown(struct pci_dev *dev)
release_region(ali1563_smba,ALI1563_SMB_IOSIZE);
}
-static struct i2c_algorithm ali1563_algorithm = {
+static const struct i2c_algorithm ali1563_algorithm = {
.smbus_xfer = ali1563_access,
.functionality = ali1563_func,
};
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index 7a5c0941dbc..3f11b6e1a34 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -463,7 +463,7 @@ static u32 ali15x3_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = ali15x3_access,
.functionality = ali15x3_func,
};
diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c
index 1750dedaf4b..2d21afdc5b1 100644
--- a/drivers/i2c/busses/i2c-amd756.c
+++ b/drivers/i2c/busses/i2c-amd756.c
@@ -294,7 +294,7 @@ static u32 amd756_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = amd756_access,
.functionality = amd756_func,
};
diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c
index e5ef560e686..0fbc7186c91 100644
--- a/drivers/i2c/busses/i2c-amd8111.c
+++ b/drivers/i2c/busses/i2c-amd8111.c
@@ -316,7 +316,7 @@ static u32 amd8111_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = amd8111_access,
.functionality = amd8111_func,
};
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index d06edce03bf..d7e7c359fc3 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -34,8 +34,7 @@
#include <linux/errno.h>
#include <linux/i2c.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-pb1x00/pb1550.h>
+#include <asm/mach-au1x00/au1xxx.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
#include "i2c-au1550.h"
@@ -118,13 +117,19 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd)
/* Reset the FIFOs, clear events.
*/
- sp->psc_smbpcr = PSC_SMBPCR_DC;
+ stat = sp->psc_smbstat;
sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
au_sync();
- do {
- stat = sp->psc_smbpcr;
+
+ if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) {
+ sp->psc_smbpcr = PSC_SMBPCR_DC;
au_sync();
- } while ((stat & PSC_SMBPCR_DC) != 0);
+ do {
+ stat = sp->psc_smbpcr;
+ au_sync();
+ } while ((stat & PSC_SMBPCR_DC) != 0);
+ udelay(50);
+ }
/* Write out the i2c chip address and specify operation
*/
@@ -279,10 +284,10 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
static u32
au1550_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm au1550_algo = {
+static const struct i2c_algorithm au1550_algo = {
.master_xfer = au1550_xfer,
.functionality = au1550_func,
};
diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c
index 59f8308c235..caa8e5c8bfb 100644
--- a/drivers/i2c/busses/i2c-elektor.c
+++ b/drivers/i2c/busses/i2c-elektor.c
@@ -196,7 +196,6 @@ static struct i2c_algo_pcf_data pcf_isa_data = {
.getclock = pcf_isa_getclock,
.waitforpin = pcf_isa_waitforpin,
.udelay = 10,
- .mdelay = 10,
.timeout = 100,
};
diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c
index e0cb3b0f92f..457d48a0ab9 100644
--- a/drivers/i2c/busses/i2c-hydra.c
+++ b/drivers/i2c/busses/i2c-hydra.c
@@ -99,7 +99,6 @@ static struct i2c_algo_bit_data hydra_bit_data = {
.getsda = hydra_bit_getsda,
.getscl = hydra_bit_getscl,
.udelay = 5,
- .mdelay = 5,
.timeout = HZ
};
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 7be1d0a3e8f..bbb2fbee836 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -434,7 +434,7 @@ static u32 i801_func(struct i2c_adapter *adapter)
| (isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0);
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = i801_access,
.functionality = i801_func,
};
diff --git a/drivers/i2c/busses/i2c-i810.c b/drivers/i2c/busses/i2c-i810.c
index 748be30f2ba..b66fb6bb187 100644
--- a/drivers/i2c/busses/i2c-i810.c
+++ b/drivers/i2c/busses/i2c-i810.c
@@ -166,7 +166,6 @@ static struct i2c_algo_bit_data i810_i2c_bit_data = {
.getsda = bit_i810i2c_getsda,
.getscl = bit_i810i2c_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
@@ -182,7 +181,6 @@ static struct i2c_algo_bit_data i810_ddc_bit_data = {
.getsda = bit_i810ddc_getsda,
.getscl = bit_i810ddc_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index 0599bbd65d9..5bccb5d6831 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -625,7 +625,7 @@ static u32 iic_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
}
-static struct i2c_algorithm iic_algo = {
+static const struct i2c_algorithm iic_algo = {
.master_xfer = iic_xfer,
.functionality = iic_func
};
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index 48c56939c86..4436c89be58 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -82,14 +82,16 @@ iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap)
/*
* Every time unit enable is asserted, GPOD needs to be cleared
- * on IOP321 to avoid data corruption on the bus.
+ * on IOP3XX to avoid data corruption on the bus.
*/
-#ifdef CONFIG_ARCH_IOP321
-#define IOP321_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */
-#define IOP321_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */
-
- *IOP321_GPOD &= (iop3xx_adap->id == 0) ? ~IOP321_GPOD_I2C0 :
- ~IOP321_GPOD_I2C1;
+#ifdef CONFIG_PLAT_IOP
+ if (iop3xx_adap->id == 0) {
+ gpio_line_set(IOP3XX_GPIO_LINE(7), GPIO_LOW);
+ gpio_line_set(IOP3XX_GPIO_LINE(6), GPIO_LOW);
+ } else {
+ gpio_line_set(IOP3XX_GPIO_LINE(5), GPIO_LOW);
+ gpio_line_set(IOP3XX_GPIO_LINE(4), GPIO_LOW);
+ }
#endif
/* NB SR bits not same position as CR IE bits :-( */
iop3xx_adap->SR_enabled =
@@ -401,7 +403,7 @@ iop3xx_i2c_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm iop3xx_i2c_algo = {
+static const struct i2c_algorithm iop3xx_i2c_algo = {
.master_xfer = iop3xx_i2c_master_xfer,
.algo_control = iop3xx_i2c_algo_control,
.functionality = iop3xx_i2c_func,
diff --git a/drivers/i2c/busses/i2c-isa.c b/drivers/i2c/busses/i2c-isa.c
index c3e1d3e888d..4380653748a 100644
--- a/drivers/i2c/busses/i2c-isa.c
+++ b/drivers/i2c/busses/i2c-isa.c
@@ -43,7 +43,7 @@
static u32 isa_func(struct i2c_adapter *adapter);
/* This is the actual algorithm we define */
-static struct i2c_algorithm isa_algorithm = {
+static const struct i2c_algorithm isa_algorithm = {
.functionality = isa_func,
};
@@ -89,9 +89,14 @@ int i2c_isa_add_driver(struct i2c_driver *driver)
dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->driver.name);
/* Now look for clients */
- driver->attach_adapter(&isa_adapter);
-
- return 0;
+ res = driver->attach_adapter(&isa_adapter);
+ if (res) {
+ dev_err(&isa_adapter.dev,
+ "Driver %s failed to attach adapter, unregistering\n",
+ driver->driver.name);
+ driver_unregister(&driver->driver);
+ }
+ return res;
}
int i2c_isa_del_driver(struct i2c_driver *driver)
@@ -125,6 +130,8 @@ int i2c_isa_del_driver(struct i2c_driver *driver)
static int __init i2c_isa_init(void)
{
+ int err;
+
mutex_init(&isa_adapter.clist_lock);
INIT_LIST_HEAD(&isa_adapter.clients);
@@ -133,8 +140,16 @@ static int __init i2c_isa_init(void)
sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr);
isa_adapter.dev.driver = &i2c_adapter_driver;
isa_adapter.dev.release = &i2c_adapter_dev_release;
- device_register(&isa_adapter.dev);
- device_create_file(&isa_adapter.dev, &dev_attr_name);
+ err = device_register(&isa_adapter.dev);
+ if (err) {
+ printk(KERN_ERR "i2c-isa: Failed to register device\n");
+ goto exit;
+ }
+ err = device_create_file(&isa_adapter.dev, &dev_attr_name);
+ if (err) {
+ printk(KERN_ERR "i2c-isa: Failed to create name file\n");
+ goto exit_unregister;
+ }
/* Add this adapter to the i2c_adapter class */
memset(&isa_adapter.class_dev, 0x00, sizeof(struct class_device));
@@ -142,11 +157,24 @@ static int __init i2c_isa_init(void)
isa_adapter.class_dev.class = &i2c_adapter_class;
strlcpy(isa_adapter.class_dev.class_id, isa_adapter.dev.bus_id,
BUS_ID_SIZE);
- class_device_register(&isa_adapter.class_dev);
+ err = class_device_register(&isa_adapter.class_dev);
+ if (err) {
+ printk(KERN_ERR "i2c-isa: Failed to register class device\n");
+ goto exit_remove_name;
+ }
dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name);
return 0;
+
+exit_remove_name:
+ device_remove_file(&isa_adapter.dev, &dev_attr_name);
+exit_unregister:
+ init_completion(&isa_adapter.dev_released); /* Needed? */
+ device_unregister(&isa_adapter.dev);
+ wait_for_completion(&isa_adapter.dev_released);
+exit:
+ return err;
}
static void __exit i2c_isa_exit(void)
diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c
index cd6f45d186a..dd3f4cd3aa6 100644
--- a/drivers/i2c/busses/i2c-ixp2000.c
+++ b/drivers/i2c/busses/i2c-ixp2000.c
@@ -114,7 +114,6 @@ static int ixp2000_i2c_probe(struct platform_device *plat_dev)
drv_data->algo_data.getsda = ixp2000_bit_getsda;
drv_data->algo_data.getscl = ixp2000_bit_getscl;
drv_data->algo_data.udelay = 6;
- drv_data->algo_data.mdelay = 6;
drv_data->algo_data.timeout = 100;
drv_data->adapter.id = I2C_HW_B_IXP2000,
diff --git a/drivers/i2c/busses/i2c-ixp4xx.c b/drivers/i2c/busses/i2c-ixp4xx.c
index 2ed07112d68..ab573254a8a 100644
--- a/drivers/i2c/busses/i2c-ixp4xx.c
+++ b/drivers/i2c/busses/i2c-ixp4xx.c
@@ -122,7 +122,6 @@ static int ixp4xx_i2c_probe(struct platform_device *plat_dev)
drv_data->algo_data.getsda = ixp4xx_bit_getsda;
drv_data->algo_data.getscl = ixp4xx_bit_getscl;
drv_data->algo_data.udelay = 10;
- drv_data->algo_data.mdelay = 10;
drv_data->algo_data.timeout = 100;
drv_data->adapter.id = I2C_HW_B_IXP4XX;
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 377ab40944b..155a986de51 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -272,7 +272,7 @@ static u32 mpc_functionality(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm mpc_algo = {
+static const struct i2c_algorithm mpc_algo = {
.master_xfer = mpc_xfer,
.functionality = mpc_functionality,
};
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index ac5cde1bbd2..eacbaf745b6 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -431,7 +431,7 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
return num;
}
-static struct i2c_algorithm mv64xxx_i2c_algo = {
+static const struct i2c_algorithm mv64xxx_i2c_algo = {
.master_xfer = mv64xxx_i2c_xfer,
.functionality = mv64xxx_i2c_functionality,
};
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index 604b49e22df..e0292e414ab 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -109,7 +109,7 @@ static s32 nforce2_access(struct i2c_adapter *adap, u16 addr,
static u32 nforce2_func(struct i2c_adapter *adapter);
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = nforce2_access,
.functionality = nforce2_func,
};
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 592824087c4..952a28d485c 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -199,7 +199,7 @@ static u32 ocores_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm ocores_algorithm = {
+static const struct i2c_algorithm ocores_algorithm = {
.master_xfer = ocores_xfer,
.functionality = ocores_func,
};
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
new file mode 100644
index 00000000000..81d87d2c2a2
--- /dev/null
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -0,0 +1,676 @@
+/*
+ * TI OMAP I2C master mode driver
+ *
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * Updated to work with multiple I2C interfaces on 24xx by
+ * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com>
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * Cleaned up by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+/* timeout waiting for the controller to respond */
+#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+#define OMAP_I2C_REV_REG 0x00
+#define OMAP_I2C_IE_REG 0x04
+#define OMAP_I2C_STAT_REG 0x08
+#define OMAP_I2C_IV_REG 0x0c
+#define OMAP_I2C_SYSS_REG 0x10
+#define OMAP_I2C_BUF_REG 0x14
+#define OMAP_I2C_CNT_REG 0x18
+#define OMAP_I2C_DATA_REG 0x1c
+#define OMAP_I2C_SYSC_REG 0x20
+#define OMAP_I2C_CON_REG 0x24
+#define OMAP_I2C_OA_REG 0x28
+#define OMAP_I2C_SA_REG 0x2c
+#define OMAP_I2C_PSC_REG 0x30
+#define OMAP_I2C_SCLL_REG 0x34
+#define OMAP_I2C_SCLH_REG 0x38
+#define OMAP_I2C_SYSTEST_REG 0x3c
+
+/* I2C Interrupt Enable Register (OMAP_I2C_IE): */
+#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */
+#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */
+#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */
+#define OMAP_I2C_IE_NACK (1 << 1) /* No ack interrupt enable */
+#define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */
+
+/* I2C Status Register (OMAP_I2C_STAT): */
+#define OMAP_I2C_STAT_SBD (1 << 15) /* Single byte data */
+#define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */
+#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */
+#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
+#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */
+#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */
+#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
+#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */
+#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */
+#define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */
+#define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */
+
+/* I2C Buffer Configuration Register (OMAP_I2C_BUF): */
+#define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */
+#define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */
+
+/* I2C Configuration Register (OMAP_I2C_CON): */
+#define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */
+#define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */
+#define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */
+#define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */
+#define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */
+#define OMAP_I2C_CON_XA (1 << 8) /* Expand address */
+#define OMAP_I2C_CON_RM (1 << 2) /* Repeat mode (master only) */
+#define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */
+#define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */
+
+/* I2C System Test Register (OMAP_I2C_SYSTEST): */
+#ifdef DEBUG
+#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
+#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
+#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */
+#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
+#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
+#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
+#endif
+
+/* I2C System Status register (OMAP_I2C_SYSS): */
+#define OMAP_I2C_SYSS_RDONE (1 << 0) /* Reset Done */
+
+/* I2C System Configuration Register (OMAP_I2C_SYSC): */
+#define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */
+
+/* REVISIT: Use platform_data instead of module parameters */
+/* Fast Mode = 400 kHz, Standard = 100 kHz */
+static int clock = 100; /* Default: 100 kHz */
+module_param(clock, int, 0);
+MODULE_PARM_DESC(clock, "Set I2C clock in kHz: 400=fast mode (default == 100)");
+
+struct omap_i2c_dev {
+ struct device *dev;
+ void __iomem *base; /* virtual */
+ int irq;
+ struct clk *iclk; /* Interface clock */
+ struct clk *fclk; /* Functional clock */
+ struct completion cmd_complete;
+ struct resource *ioarea;
+ u16 cmd_err;
+ u8 *buf;
+ size_t buf_len;
+ struct i2c_adapter adapter;
+ unsigned rev1:1;
+};
+
+static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
+ int reg, u16 val)
+{
+ __raw_writew(val, i2c_dev->base + reg);
+}
+
+static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
+{
+ return __raw_readw(i2c_dev->base + reg);
+}
+
+static int omap_i2c_get_clocks(struct omap_i2c_dev *dev)
+{
+ if (cpu_is_omap16xx() || cpu_is_omap24xx()) {
+ dev->iclk = clk_get(dev->dev, "i2c_ick");
+ if (IS_ERR(dev->iclk)) {
+ dev->iclk = NULL;
+ return -ENODEV;
+ }
+ }
+
+ dev->fclk = clk_get(dev->dev, "i2c_fck");
+ if (IS_ERR(dev->fclk)) {
+ if (dev->iclk != NULL) {
+ clk_put(dev->iclk);
+ dev->iclk = NULL;
+ }
+ dev->fclk = NULL;
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void omap_i2c_put_clocks(struct omap_i2c_dev *dev)
+{
+ clk_put(dev->fclk);
+ dev->fclk = NULL;
+ if (dev->iclk != NULL) {
+ clk_put(dev->iclk);
+ dev->iclk = NULL;
+ }
+}
+
+static void omap_i2c_enable_clocks(struct omap_i2c_dev *dev)
+{
+ if (dev->iclk != NULL)
+ clk_enable(dev->iclk);
+ clk_enable(dev->fclk);
+}
+
+static void omap_i2c_disable_clocks(struct omap_i2c_dev *dev)
+{
+ if (dev->iclk != NULL)
+ clk_disable(dev->iclk);
+ clk_disable(dev->fclk);
+}
+
+static int omap_i2c_init(struct omap_i2c_dev *dev)
+{
+ u16 psc = 0;
+ unsigned long fclk_rate = 12000000;
+ unsigned long timeout;
+
+ if (!dev->rev1) {
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, OMAP_I2C_SYSC_SRST);
+ /* For some reason we need to set the EN bit before the
+ * reset done bit gets set. */
+ timeout = jiffies + OMAP_I2C_TIMEOUT;
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+ while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) &
+ OMAP_I2C_SYSS_RDONE)) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(dev->dev, "timeout waiting"
+ "for controller reset\n");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+
+ if (cpu_class_is_omap1()) {
+ struct clk *armxor_ck;
+
+ armxor_ck = clk_get(NULL, "armxor_ck");
+ if (IS_ERR(armxor_ck))
+ dev_warn(dev->dev, "Could not get armxor_ck\n");
+ else {
+ fclk_rate = clk_get_rate(armxor_ck);
+ clk_put(armxor_ck);
+ }
+ /* TRM for 5912 says the I2C clock must be prescaled to be
+ * between 7 - 12 MHz. The XOR input clock is typically
+ * 12, 13 or 19.2 MHz. So we should have code that produces:
+ *
+ * XOR MHz Divider Prescaler
+ * 12 1 0
+ * 13 2 1
+ * 19.2 2 1
+ */
+ if (fclk_rate > 16000000)
+ psc = (fclk_rate + 8000000) / 12000000;
+ }
+
+ /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */
+ omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, psc);
+
+ /* Program desired operating rate */
+ fclk_rate /= (psc + 1) * 1000;
+ if (psc > 2)
+ psc = 2;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG,
+ fclk_rate / (clock * 2) - 7 + psc);
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG,
+ fclk_rate / (clock * 2) - 7 + psc);
+
+ /* Take the I2C module out of reset: */
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+
+ /* Enable interrupts */
+ omap_i2c_write_reg(dev, OMAP_I2C_IE_REG,
+ (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
+ OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
+ OMAP_I2C_IE_AL));
+ return 0;
+}
+
+/*
+ * Waiting on Bus Busy
+ */
+static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + OMAP_I2C_TIMEOUT;
+ while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(dev->dev, "timeout waiting for bus ready\n");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ return 0;
+}
+
+/*
+ * Low level master read/write transaction.
+ */
+static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ int r;
+ u16 w;
+
+ dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+
+ if (msg->len == 0)
+ return -EINVAL;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
+
+ /* REVISIT: Could the STB bit of I2C_CON be used with probing? */
+ dev->buf = msg->buf;
+ dev->buf_len = msg->len;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
+ if (msg->flags & I2C_M_TEN)
+ w |= OMAP_I2C_CON_XA;
+ if (!(msg->flags & I2C_M_RD))
+ w |= OMAP_I2C_CON_TRX;
+ if (stop)
+ w |= OMAP_I2C_CON_STP;
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+
+ r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
+ OMAP_I2C_TIMEOUT);
+ dev->buf_len = 0;
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ dev_err(dev->dev, "controller timed out\n");
+ omap_i2c_init(dev);
+ return -ETIMEDOUT;
+ }
+
+ if (likely(!dev->cmd_err))
+ return 0;
+
+ /* We have an error */
+ if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR |
+ OMAP_I2C_STAT_XUDF)) {
+ omap_i2c_init(dev);
+ return -EIO;
+ }
+
+ if (dev->cmd_err & OMAP_I2C_STAT_NACK) {
+ if (msg->flags & I2C_M_IGNORE_NAK)
+ return 0;
+ if (stop) {
+ w = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG);
+ w |= OMAP_I2C_CON_STP;
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+ }
+ return -EREMOTEIO;
+ }
+ return -EIO;
+}
+
+
+/*
+ * Prepare controller for a transaction and call omap_i2c_xfer_msg
+ * to do the work during IRQ processing.
+ */
+static int
+omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ int i;
+ int r;
+
+ omap_i2c_enable_clocks(dev);
+
+ /* REVISIT: initialize and use adap->retries. This is an optional
+ * feature */
+ if ((r = omap_i2c_wait_for_bb(dev)) < 0)
+ goto out;
+
+ for (i = 0; i < num; i++) {
+ r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (r != 0)
+ break;
+ }
+
+ if (r == 0)
+ r = num;
+out:
+ omap_i2c_disable_clocks(dev);
+ return r;
+}
+
+static u32
+omap_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static inline void
+omap_i2c_complete_cmd(struct omap_i2c_dev *dev, u16 err)
+{
+ dev->cmd_err |= err;
+ complete(&dev->cmd_complete);
+}
+
+static inline void
+omap_i2c_ack_stat(struct omap_i2c_dev *dev, u16 stat)
+{
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat);
+}
+
+static irqreturn_t
+omap_i2c_rev1_isr(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+ struct omap_i2c_dev *dev = dev_id;
+ u16 iv, w;
+
+ iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG);
+ switch (iv) {
+ case 0x00: /* None */
+ break;
+ case 0x01: /* Arbitration lost */
+ dev_err(dev->dev, "Arbitration lost\n");
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL);
+ break;
+ case 0x02: /* No acknowledgement */
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_STP);
+ break;
+ case 0x03: /* Register access ready */
+ omap_i2c_complete_cmd(dev, 0);
+ break;
+ case 0x04: /* Receive data ready */
+ if (dev->buf_len) {
+ w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+ *dev->buf++ = w;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ *dev->buf++ = w >> 8;
+ dev->buf_len--;
+ }
+ } else
+ dev_err(dev->dev, "RRDY IRQ while no data requested\n");
+ break;
+ case 0x05: /* Transmit data ready */
+ if (dev->buf_len) {
+ w = *dev->buf++;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ w |= *dev->buf++ << 8;
+ dev->buf_len--;
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+ } else
+ dev_err(dev->dev, "XRDY IRQ while no data to send\n");
+ break;
+ default:
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+omap_i2c_isr(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+ struct omap_i2c_dev *dev = dev_id;
+ u16 bits;
+ u16 stat, w;
+ int count = 0;
+
+ bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
+ while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
+ dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
+ if (count++ == 100) {
+ dev_warn(dev->dev, "Too much work in one IRQ\n");
+ break;
+ }
+
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat);
+
+ if (stat & OMAP_I2C_STAT_ARDY) {
+ omap_i2c_complete_cmd(dev, 0);
+ continue;
+ }
+ if (stat & OMAP_I2C_STAT_RRDY) {
+ w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+ if (dev->buf_len) {
+ *dev->buf++ = w;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ *dev->buf++ = w >> 8;
+ dev->buf_len--;
+ }
+ } else
+ dev_err(dev->dev, "RRDY IRQ while no data"
+ "requested\n");
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY);
+ continue;
+ }
+ if (stat & OMAP_I2C_STAT_XRDY) {
+ w = 0;
+ if (dev->buf_len) {
+ w = *dev->buf++;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ w |= *dev->buf++ << 8;
+ dev->buf_len--;
+ }
+ } else
+ dev_err(dev->dev, "XRDY IRQ while no"
+ "data to send\n");
+ omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY);
+ continue;
+ }
+ if (stat & OMAP_I2C_STAT_ROVR) {
+ dev_err(dev->dev, "Receive overrun\n");
+ dev->cmd_err |= OMAP_I2C_STAT_ROVR;
+ }
+ if (stat & OMAP_I2C_STAT_XUDF) {
+ dev_err(dev->dev, "Transmit overflow\n");
+ dev->cmd_err |= OMAP_I2C_STAT_XUDF;
+ }
+ if (stat & OMAP_I2C_STAT_NACK) {
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
+ OMAP_I2C_CON_STP);
+ }
+ if (stat & OMAP_I2C_STAT_AL) {
+ dev_err(dev->dev, "Arbitration lost\n");
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL);
+ }
+ }
+
+ return count ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static const struct i2c_algorithm omap_i2c_algo = {
+ .master_xfer = omap_i2c_xfer,
+ .functionality = omap_i2c_func,
+};
+
+static int
+omap_i2c_probe(struct platform_device *pdev)
+{
+ struct omap_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *mem, *irq, *ioarea;
+ int r;
+
+ /* NOTE: driver uses the static register mapping */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return -ENODEV;
+ }
+
+ ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+
+ if (clock > 200)
+ clock = 400; /* Fast mode */
+ else
+ clock = 100; /* Standard mode */
+
+ dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+ dev->dev = &pdev->dev;
+ dev->irq = irq->start;
+ dev->base = (void __iomem *) IO_ADDRESS(mem->start);
+ platform_set_drvdata(pdev, dev);
+
+ if ((r = omap_i2c_get_clocks(dev)) != 0)
+ goto err_free_mem;
+
+ omap_i2c_enable_clocks(dev);
+
+ if (cpu_is_omap15xx())
+ dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20;
+
+ /* reset ASAP, clearing any IRQs */
+ omap_i2c_init(dev);
+
+ r = request_irq(dev->irq, dev->rev1 ? omap_i2c_rev1_isr : omap_i2c_isr,
+ 0, pdev->name, dev);
+
+ if (r) {
+ dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
+ goto err_unuse_clocks;
+ }
+ r = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
+ dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n",
+ pdev->id, r >> 4, r & 0xf, clock);
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
+ adap->algo = &omap_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ /* i2c device drivers may be active on return from add_adapter() */
+ r = i2c_add_adapter(adap);
+ if (r) {
+ dev_err(dev->dev, "failure adding adapter\n");
+ goto err_free_irq;
+ }
+
+ omap_i2c_disable_clocks(dev);
+
+ return 0;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+err_unuse_clocks:
+ omap_i2c_disable_clocks(dev);
+ omap_i2c_put_clocks(dev);
+err_free_mem:
+ platform_set_drvdata(pdev, NULL);
+ kfree(dev);
+err_release_region:
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+ return r;
+}
+
+static int
+omap_i2c_remove(struct platform_device *pdev)
+{
+ struct omap_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(dev->irq, dev);
+ i2c_del_adapter(&dev->adapter);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+ omap_i2c_put_clocks(dev);
+ kfree(dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return 0;
+}
+
+static struct platform_driver omap_i2c_driver = {
+ .probe = omap_i2c_probe,
+ .remove = omap_i2c_remove,
+ .driver = {
+ .name = "i2c_omap",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* I2C may be needed to bring up other drivers */
+static int __init
+omap_i2c_init_driver(void)
+{
+ return platform_driver_register(&omap_i2c_driver);
+}
+subsys_initcall(omap_i2c_init_driver);
+
+static void __exit omap_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&omap_i2c_driver);
+}
+module_exit(omap_i2c_exit_driver);
+
+MODULE_AUTHOR("MontaVista Software, Inc. (and others)");
+MODULE_DESCRIPTION("TI OMAP I2C bus adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
index e09ebbb2f9f..5eb2bd294fd 100644
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ b/drivers/i2c/busses/i2c-parport-light.c
@@ -103,7 +103,6 @@ static struct i2c_algo_bit_data parport_algo_data = {
.getsda = parport_getsda,
.getscl = parport_getscl,
.udelay = 50,
- .mdelay = 50,
.timeout = HZ,
};
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index 934bd55bae1..48a829431c7 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -138,7 +138,6 @@ static struct i2c_algo_bit_data parport_algo_data = {
.getsda = parport_getsda,
.getscl = parport_getscl,
.udelay = 60,
- .mdelay = 60,
.timeout = HZ,
};
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 8f2f65b793b..30c7a1b38cb 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -376,7 +376,7 @@ static u32 piix4_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = piix4_access,
.functionality = piix4_func,
};
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c
index d658d910795..a508cb962d2 100644
--- a/drivers/i2c/busses/i2c-powermac.c
+++ b/drivers/i2c/busses/i2c-powermac.c
@@ -175,7 +175,7 @@ static u32 i2c_powermac_func(struct i2c_adapter * adapter)
}
/* For now, we only handle smbus */
-static struct i2c_algorithm i2c_powermac_algorithm = {
+static const struct i2c_algorithm i2c_powermac_algorithm = {
.smbus_xfer = i2c_powermac_smbus_xfer,
.master_xfer = i2c_powermac_master_xfer,
.functionality = i2c_powermac_func,
diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c
index 9479525892e..7745e21874a 100644
--- a/drivers/i2c/busses/i2c-prosavage.c
+++ b/drivers/i2c/busses/i2c-prosavage.c
@@ -180,7 +180,6 @@ static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iom
p->algo.getsda = bit_s3via_getsda;
p->algo.getscl = bit_s3via_getscl;
p->algo.udelay = CYCLE_DELAY;
- p->algo.mdelay = CYCLE_DELAY;
p->algo.timeout = TIMEOUT;
p->algo.data = p;
p->mmvga = mmvga;
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index ee114b48fac..cd4ad98ad51 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -926,7 +926,7 @@ static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm i2c_pxa_algorithm = {
+static const struct i2c_algorithm i2c_pxa_algorithm = {
.master_xfer = i2c_pxa_xfer,
.functionality = i2c_pxa_functionality,
};
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 5d2950e91fc..9ebe429a0a0 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -566,7 +566,7 @@ static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
/* i2c bus registration info */
-static struct i2c_algorithm s3c24xx_i2c_algorithm = {
+static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
diff --git a/drivers/i2c/busses/i2c-savage4.c b/drivers/i2c/busses/i2c-savage4.c
index 0c8518298e4..209f47ea175 100644
--- a/drivers/i2c/busses/i2c-savage4.c
+++ b/drivers/i2c/busses/i2c-savage4.c
@@ -140,7 +140,6 @@ static struct i2c_algo_bit_data sav_i2c_bit_data = {
.getsda = bit_savi2c_getsda,
.getscl = bit_savi2c_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
index fa503ed9f86..0ca599d3b40 100644
--- a/drivers/i2c/busses/i2c-sibyte.c
+++ b/drivers/i2c/busses/i2c-sibyte.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2004 Steven J. Hill
* Copyright (C) 2001,2002,2003 Broadcom Corporation
+ * Copyright (C) 1995-2000 Simon G. Vogl
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -17,11 +18,162 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/i2c-algo-sibyte.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <asm/io.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_smbus.h>
+
+struct i2c_algo_sibyte_data {
+ void *data; /* private data */
+ int bus; /* which bus */
+ void *reg_base; /* CSR base */
+};
+
+/* ----- global defines ----------------------------------------------- */
+#define SMB_CSR(a,r) ((long)(a->reg_base + r))
+
+/* ----- global variables --------------------------------------------- */
+
+/* module parameters:
+ */
+static int bit_scan; /* have a look at what's hanging 'round */
+module_param(bit_scan, int, 0);
+MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus");
+
+
+static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data * data)
+{
+ struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+ int data_bytes = 0;
+ int error;
+
+ while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ csr_out32((V_SMB_ADDR(addr) |
+ (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
+ V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
+ break;
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_READ) {
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ data_bytes = 1;
+ } else {
+ csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ }
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+ if (read_write == I2C_SMBUS_READ) {
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ data_bytes = 1;
+ } else {
+ csr_out32(V_SMB_LB(data->byte),
+ SMB_CSR(adap, R_SMB_DATA));
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ }
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+ if (read_write == I2C_SMBUS_READ) {
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ data_bytes = 2;
+ } else {
+ csr_out32(V_SMB_LB(data->word & 0xff),
+ SMB_CSR(adap, R_SMB_DATA));
+ csr_out32(V_SMB_MB(data->word >> 8),
+ SMB_CSR(adap, R_SMB_DATA));
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ }
+ break;
+ default:
+ return -1; /* XXXKW better error code? */
+ }
+
+ while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
+ if (error & M_SMB_ERROR) {
+ /* Clear error bit by writing a 1 */
+ csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
+ return -1; /* XXXKW better error code? */
+ }
+
+ if (data_bytes == 1)
+ data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
+ if (data_bytes == 2)
+ data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
+
+ return 0;
+}
+
+static u32 bit_func(struct i2c_adapter *adap)
+{
+ return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
+}
+
+
+/* -----exported algorithm data: ------------------------------------- */
+
+static const struct i2c_algorithm i2c_sibyte_algo = {
+ .smbus_xfer = smbus_xfer,
+ .functionality = bit_func,
+};
+
+/*
+ * registering functions to load algorithms at runtime
+ */
+int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
+{
+ int i;
+ struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+
+ /* register new adapter to i2c module... */
+ i2c_adap->algo = &i2c_sibyte_algo;
+
+ /* Set the frequency to 100 kHz */
+ csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
+ csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
+
+ /* scan bus */
+ if (bit_scan) {
+ union i2c_smbus_data data;
+ int rc;
+ printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n",
+ i2c_adap->name);
+ for (i = 0x00; i < 0x7f; i++) {
+ /* XXXKW is this a realistic probe? */
+ rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (!rc) {
+ printk("(%02x)",i);
+ } else
+ printk(".");
+ }
+ printk("\n");
+ }
+
+ return i2c_add_adapter(i2c_adap);
+}
+
+
static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
{ NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) },
{ NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) }
@@ -58,13 +210,13 @@ static int __init i2c_sibyte_init(void)
static void __exit i2c_sibyte_exit(void)
{
- i2c_sibyte_del_bus(&sibyte_board_adapter[0]);
- i2c_sibyte_del_bus(&sibyte_board_adapter[1]);
+ i2c_del_adapter(&sibyte_board_adapter[0]);
+ i2c_del_adapter(&sibyte_board_adapter[1]);
}
module_init(i2c_sibyte_init);
module_exit(i2c_sibyte_exit);
-MODULE_AUTHOR("Kip Walker <kwalker@broadcom.com>, Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Kip Walker (Broadcom Corp.), Steven J. Hill <sjhill@realitydiluted.com>");
MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index b57ab74d23e..38bbfd840b6 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -358,7 +358,7 @@ static u32 sis5595_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_PROC_CALL;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = sis5595_access,
.functionality = sis5595_func,
};
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index acb75e28241..dec0bafb52a 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -450,7 +450,7 @@ exit:
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = sis630_access,
.functionality = sis630_func,
};
diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c
index 1a73c0532fc..7fd07fbac33 100644
--- a/drivers/i2c/busses/i2c-sis96x.c
+++ b/drivers/i2c/busses/i2c-sis96x.c
@@ -242,7 +242,7 @@ static u32 sis96x_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_PROC_CALL;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = sis96x_access,
.functionality = sis96x_func,
};
diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c
index 73f481e93a3..a54adc50d16 100644
--- a/drivers/i2c/busses/i2c-stub.c
+++ b/drivers/i2c/busses/i2c-stub.c
@@ -27,6 +27,10 @@
#include <linux/errno.h>
#include <linux/i2c.h>
+static unsigned short chip_addr;
+module_param(chip_addr, ushort, S_IRUGO);
+MODULE_PARM_DESC(chip_addr, "Chip address (between 0x03 and 0x77)\n");
+
static u8 stub_pointer;
static u8 stub_bytes[256];
static u16 stub_words[256];
@@ -37,6 +41,9 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
{
s32 ret;
+ if (addr != chip_addr)
+ return -ENODEV;
+
switch (size) {
case I2C_SMBUS_QUICK:
@@ -108,7 +115,7 @@ static u32 stub_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.functionality = stub_func,
.smbus_xfer = stub_xfer,
};
@@ -122,7 +129,17 @@ static struct i2c_adapter stub_adapter = {
static int __init i2c_stub_init(void)
{
- printk(KERN_INFO "i2c-stub loaded\n");
+ if (!chip_addr) {
+ printk(KERN_ERR "i2c-stub: Please specify a chip address\n");
+ return -ENODEV;
+ }
+ if (chip_addr < 0x03 || chip_addr > 0x77) {
+ printk(KERN_ERR "i2c-stub: Invalid chip address 0x%02x\n",
+ chip_addr);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", chip_addr);
return i2c_add_adapter(&stub_adapter);
}
diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c
index 484bbacfce6..910e200ad50 100644
--- a/drivers/i2c/busses/i2c-via.c
+++ b/drivers/i2c/busses/i2c-via.c
@@ -81,7 +81,6 @@ static struct i2c_algo_bit_data bit_data = {
.getsda = bit_via_getsda,
.getscl = bit_via_getscl,
.udelay = 5,
- .mdelay = 5,
.timeout = HZ
};
diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c
index 47e52bf2c5e..efc6bbf0cc0 100644
--- a/drivers/i2c/busses/i2c-viapro.c
+++ b/drivers/i2c/busses/i2c-viapro.c
@@ -34,6 +34,8 @@
VT8233A 0x3147 yes?
VT8235 0x3177 yes
VT8237R 0x3227 yes
+ VT8237A 0x3337 yes
+ VT8251 0x3287 yes
Note: we assume there can only be one device, with one SMBus interface.
*/
@@ -297,7 +299,7 @@ static u32 vt596_func(struct i2c_adapter *adapter)
return func;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = vt596_access,
.functionality = vt596_func,
};
@@ -381,7 +383,9 @@ found:
dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba);
switch (pdev->device) {
+ case PCI_DEVICE_ID_VIA_8251:
case PCI_DEVICE_ID_VIA_8237:
+ case PCI_DEVICE_ID_VIA_8237A:
case PCI_DEVICE_ID_VIA_8235:
case PCI_DEVICE_ID_VIA_8233A:
case PCI_DEVICE_ID_VIA_8233_0:
@@ -432,8 +436,12 @@ static struct pci_device_id vt596_ids[] = {
.driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237),
.driver_data = SMBBA3 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237A),
+ .driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4),
.driver_data = SMBBA1 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8251),
+ .driver_data = SMBBA3 },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c
index b675773b0cc..6c8d2518338 100644
--- a/drivers/i2c/busses/i2c-voodoo3.c
+++ b/drivers/i2c/busses/i2c-voodoo3.c
@@ -160,7 +160,6 @@ static struct i2c_algo_bit_data voo_i2c_bit_data = {
.getsda = bit_vooi2c_getsda,
.getscl = bit_vooi2c_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
@@ -177,7 +176,6 @@ static struct i2c_algo_bit_data voo_ddc_bit_data = {
.getsda = bit_vooddc_getsda,
.getscl = bit_vooddc_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index eae9e81be37..32aab0d34ee 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -383,7 +383,7 @@ static u32 scx200_acb_func(struct i2c_adapter *adapter)
}
/* For now, we only handle combined mode (smbus) */
-static struct i2c_algorithm scx200_acb_algorithm = {
+static const struct i2c_algorithm scx200_acb_algorithm = {
.smbus_xfer = scx200_acb_smbus_xfer,
.functionality = scx200_acb_func,
};
diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c
index cb3ef5ac99f..8b65a5cf825 100644
--- a/drivers/i2c/busses/scx200_i2c.c
+++ b/drivers/i2c/busses/scx200_i2c.c
@@ -71,12 +71,12 @@ static int scx200_i2c_getsda(void *data)
*/
static struct i2c_algo_bit_data scx200_i2c_data = {
- NULL,
- scx200_i2c_setsda,
- scx200_i2c_setscl,
- scx200_i2c_getsda,
- scx200_i2c_getscl,
- 10, 10, 100, /* waits, timeout */
+ .setsda = scx200_i2c_setsda,
+ .setscl = scx200_i2c_setscl,
+ .getsda = scx200_i2c_getsda,
+ .getscl = scx200_i2c_getscl,
+ .udelay = 10,
+ .timeout = 100,
};
static struct i2c_adapter scx200_i2c_ops = {
diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c
index 13c108269a6..cec3a0c3894 100644
--- a/drivers/i2c/chips/eeprom.c
+++ b/drivers/i2c/chips/eeprom.c
@@ -209,10 +209,14 @@ static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* create the sysfs eeprom file */
- sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
+ err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
+ if (err)
+ goto exit_detach;
return 0;
+exit_detach:
+ i2c_detach_client(new_client);
exit_kfree:
kfree(data);
exit:
@@ -223,6 +227,8 @@ static int eeprom_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
+
err = i2c_detach_client(client);
if (err)
return err;
diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c
index f92505b94c6..182f0495346 100644
--- a/drivers/i2c/chips/isp1301_omap.c
+++ b/drivers/i2c/chips/isp1301_omap.c
@@ -30,7 +30,7 @@
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
#include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c
index 88d2ddee449..76645c14297 100644
--- a/drivers/i2c/chips/max6875.c
+++ b/drivers/i2c/chips/max6875.c
@@ -199,8 +199,7 @@ static int max6875_detect(struct i2c_adapter *adapter, int address, int kind)
mutex_init(&data->update_lock);
/* Init fake client data */
- /* set the client data to the i2c_client so that it will get freed */
- i2c_set_clientdata(fake_client, fake_client);
+ i2c_set_clientdata(fake_client, NULL);
fake_client->addr = address | 1;
fake_client->adapter = adapter;
fake_client->driver = &max6875_driver;
@@ -214,13 +213,17 @@ static int max6875_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_kfree2;
if ((err = i2c_attach_client(fake_client)) != 0)
- goto exit_detach;
+ goto exit_detach1;
- sysfs_create_bin_file(&real_client->dev.kobj, &user_eeprom_attr);
+ err = sysfs_create_bin_file(&real_client->dev.kobj, &user_eeprom_attr);
+ if (err)
+ goto exit_detach2;
return 0;
-exit_detach:
+exit_detach2:
+ i2c_detach_client(fake_client);
+exit_detach1:
i2c_detach_client(real_client);
exit_kfree2:
kfree(fake_client);
@@ -229,14 +232,24 @@ exit_kfree1:
return err;
}
+/* Will be called for both the real client and the fake client */
static int max6875_detach_client(struct i2c_client *client)
{
int err;
+ struct max6875_data *data = i2c_get_clientdata(client);
+
+ /* data is NULL for the fake client */
+ if (data)
+ sysfs_remove_bin_file(&client->dev.kobj, &user_eeprom_attr);
err = i2c_detach_client(client);
if (err)
return err;
- kfree(i2c_get_clientdata(client));
+
+ if (data) /* real client */
+ kfree(data);
+ else /* fake client */
+ kfree(client);
return 0;
}
diff --git a/drivers/i2c/chips/pca9539.c b/drivers/i2c/chips/pca9539.c
index cb22280cdd2..f43c4e79b55 100644
--- a/drivers/i2c/chips/pca9539.c
+++ b/drivers/i2c/chips/pca9539.c
@@ -148,11 +148,16 @@ static int pca9539_detect(struct i2c_adapter *adapter, int address, int kind)
if ((err = i2c_attach_client(new_client)))
goto exit_kfree;
- /* Register sysfs hooks (don't care about failure) */
- sysfs_create_group(&new_client->dev.kobj, &pca9539_defattr_group);
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&new_client->dev.kobj,
+ &pca9539_defattr_group);
+ if (err)
+ goto exit_detach;
return 0;
+exit_detach:
+ i2c_detach_client(new_client);
exit_kfree:
kfree(data);
exit:
@@ -163,6 +168,8 @@ static int pca9539_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_group(&client->dev.kobj, &pca9539_defattr_group);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/chips/pcf8574.c b/drivers/i2c/chips/pcf8574.c
index c3e6449c448..32b25427eab 100644
--- a/drivers/i2c/chips/pcf8574.c
+++ b/drivers/i2c/chips/pcf8574.c
@@ -105,6 +105,16 @@ static ssize_t set_write(struct device *dev, struct device_attribute *attr, cons
static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
+static struct attribute *pcf8574_attributes[] = {
+ &dev_attr_read.attr,
+ &dev_attr_write.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8574_attr_group = {
+ .attrs = pcf8574_attributes,
+};
+
/*
* Real code
*/
@@ -166,13 +176,13 @@ static int pcf8574_detect(struct i2c_adapter *adapter, int address, int kind)
pcf8574_init_client(new_client);
/* Register sysfs hooks */
- device_create_file(&new_client->dev, &dev_attr_read);
- device_create_file(&new_client->dev, &dev_attr_write);
+ err = sysfs_create_group(&new_client->dev.kobj, &pcf8574_attr_group);
+ if (err)
+ goto exit_detach;
return 0;
-/* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
-
+ exit_detach:
+ i2c_detach_client(new_client);
exit_free:
kfree(data);
exit:
@@ -183,6 +193,8 @@ static int pcf8574_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_group(&client->dev.kobj, &pcf8574_attr_group);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c
index 925a6b371fd..4dc36376eb3 100644
--- a/drivers/i2c/chips/pcf8591.c
+++ b/drivers/i2c/chips/pcf8591.c
@@ -158,6 +158,28 @@ static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr
static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO,
show_out0_enable, set_out0_enable);
+static struct attribute *pcf8591_attributes[] = {
+ &dev_attr_out0_enable.attr,
+ &dev_attr_out0_output.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group = {
+ .attrs = pcf8591_attributes,
+};
+
+static struct attribute *pcf8591_attributes_opt[] = {
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group_opt = {
+ .attrs = pcf8591_attributes_opt,
+};
+
/*
* Real code
*/
@@ -211,24 +233,31 @@ static int pcf8591_detect(struct i2c_adapter *adapter, int address, int kind)
pcf8591_init_client(new_client);
/* Register sysfs hooks */
- device_create_file(&new_client->dev, &dev_attr_out0_enable);
- device_create_file(&new_client->dev, &dev_attr_out0_output);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
+ err = sysfs_create_group(&new_client->dev.kobj, &pcf8591_attr_group);
+ if (err)
+ goto exit_detach;
/* Register input2 if not in "two differential inputs" mode */
- if (input_mode != 3 )
- device_create_file(&new_client->dev, &dev_attr_in2_input);
-
+ if (input_mode != 3) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in2_input)))
+ goto exit_sysfs_remove;
+ }
+
/* Register input3 only in "four single ended inputs" mode */
- if (input_mode == 0)
- device_create_file(&new_client->dev, &dev_attr_in3_input);
-
+ if (input_mode == 0) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in3_input)))
+ goto exit_sysfs_remove;
+ }
+
return 0;
-
- /* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
+exit_sysfs_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &pcf8591_attr_group_opt);
+ sysfs_remove_group(&new_client->dev.kobj, &pcf8591_attr_group);
+exit_detach:
+ i2c_detach_client(new_client);
exit_kfree:
kfree(data);
exit:
@@ -239,6 +268,9 @@ static int pcf8591_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
index 0be6fd6a267..6a757821717 100644
--- a/drivers/i2c/chips/tps65010.c
+++ b/drivers/i2c/chips/tps65010.c
@@ -305,7 +305,7 @@ static int dbg_show(struct seq_file *s, void *_)
static int dbg_tps_open(struct inode *inode, struct file *file)
{
- return single_open(file, dbg_show, inode->u.generic_ip);
+ return single_open(file, dbg_show, inode->i_private);
}
static struct file_operations debug_fops = {
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 9cb277d6aa4..01233f0f777 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -183,15 +183,21 @@ int i2c_add_adapter(struct i2c_adapter *adap)
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.driver = &i2c_adapter_driver;
adap->dev.release = &i2c_adapter_dev_release;
- device_register(&adap->dev);
- device_create_file(&adap->dev, &dev_attr_name);
+ res = device_register(&adap->dev);
+ if (res)
+ goto out_list;
+ res = device_create_file(&adap->dev, &dev_attr_name);
+ if (res)
+ goto out_unregister;
/* Add this adapter to the i2c_adapter class */
memset(&adap->class_dev, 0x00, sizeof(struct class_device));
adap->class_dev.dev = &adap->dev;
adap->class_dev.class = &i2c_adapter_class;
strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
- class_device_register(&adap->class_dev);
+ res = class_device_register(&adap->class_dev);
+ if (res)
+ goto out_remove_name;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
@@ -206,6 +212,17 @@ int i2c_add_adapter(struct i2c_adapter *adap)
out_unlock:
mutex_unlock(&core_lists);
return res;
+
+out_remove_name:
+ device_remove_file(&adap->dev, &dev_attr_name);
+out_unregister:
+ init_completion(&adap->dev_released); /* Needed? */
+ device_unregister(&adap->dev);
+ wait_for_completion(&adap->dev_released);
+out_list:
+ list_del(&adap->list);
+ idr_remove(&i2c_adapter_idr, adap->nr);
+ goto out_unlock;
}
@@ -394,14 +411,14 @@ int i2c_check_addr(struct i2c_adapter *adapter, int addr)
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
+ int res = 0;
mutex_lock(&adapter->clist_lock);
if (__i2c_check_addr(client->adapter, client->addr)) {
- mutex_unlock(&adapter->clist_lock);
- return -EBUSY;
+ res = -EBUSY;
+ goto out_unlock;
}
list_add_tail(&client->list,&adapter->clients);
- mutex_unlock(&adapter->clist_lock);
if (adapter->client_register) {
if (adapter->client_register(client)) {
@@ -422,10 +439,26 @@ int i2c_attach_client(struct i2c_client *client)
"%d-%04x", i2c_adapter_id(adapter), client->addr);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, client->dev.bus_id);
- device_register(&client->dev);
- device_create_file(&client->dev, &dev_attr_client_name);
-
- return 0;
+ res = device_register(&client->dev);
+ if (res)
+ goto out_list;
+ res = device_create_file(&client->dev, &dev_attr_client_name);
+ if (res)
+ goto out_unregister;
+
+out_unlock:
+ mutex_unlock(&adapter->clist_lock);
+ return res;
+
+out_unregister:
+ init_completion(&client->released); /* Needed? */
+ device_unregister(&client->dev);
+ wait_for_completion(&client->released);
+out_list:
+ list_del(&client->list);
+ dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
+ "(%d)\n", client->name, client->addr, res);
+ goto out_unlock;
}
@@ -674,11 +707,16 @@ static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
/* Finally call the custom detection function */
err = found_proc(adapter, addr, kind);
-
/* -ENODEV can be returned if there is a chip at the given address
but it isn't supported by this chip driver. We catch it here as
this isn't an error. */
- return (err == -ENODEV) ? 0 : err;
+ if (err == -ENODEV)
+ err = 0;
+
+ if (err)
+ dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
+ addr, err);
+ return err;
}
int i2c_probe(struct i2c_adapter *adapter,
@@ -868,7 +906,7 @@ s32 i2c_smbus_read_byte(struct i2c_client *client)
I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data))
return -1;
else
- return 0x0FF & data.byte;
+ return data.byte;
}
s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value)
@@ -884,7 +922,7 @@ s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command)
I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data))
return -1;
else
- return 0x0FF & data.byte;
+ return data.byte;
}
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)
@@ -903,7 +941,7 @@ s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command)
I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data))
return -1;
else
- return 0x0FFFF & data.word;
+ return data.word;
}
s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
@@ -1006,7 +1044,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
else {
msg[0].len=3;
msgbuf0[1] = data->word & 0xff;
- msgbuf0[2] = (data->word >> 8) & 0xff;
+ msgbuf0[2] = data->word >> 8;
}
break;
case I2C_SMBUS_PROC_CALL:
@@ -1015,7 +1053,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
msg[0].len = 3;
msg[1].len = 2;
msgbuf0[1] = data->word & 0xff;
- msgbuf0[2] = (data->word >> 8) & 0xff;
+ msgbuf0[2] = data->word >> 8;
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 58ccddd5c23..3f869033ed7 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -32,43 +32,35 @@
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
+#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
-#include <linux/platform_device.h>
#include <asm/uaccess.h>
-static struct i2c_client i2cdev_client_template;
+static struct i2c_driver i2cdev_driver;
struct i2c_dev {
- int minor;
+ struct list_head list;
struct i2c_adapter *adap;
struct class_device *class_dev;
};
-#define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev)
#define I2C_MINORS 256
-static struct i2c_dev *i2c_dev_array[I2C_MINORS];
-static DEFINE_SPINLOCK(i2c_dev_array_lock);
+static LIST_HEAD(i2c_dev_list);
+static DEFINE_SPINLOCK(i2c_dev_list_lock);
static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
struct i2c_dev *i2c_dev;
- spin_lock(&i2c_dev_array_lock);
- i2c_dev = i2c_dev_array[index];
- spin_unlock(&i2c_dev_array_lock);
- return i2c_dev;
-}
-
-static struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap)
-{
- struct i2c_dev *i2c_dev = NULL;
-
- spin_lock(&i2c_dev_array_lock);
- if ((i2c_dev_array[adap->nr]) &&
- (i2c_dev_array[adap->nr]->adap == adap))
- i2c_dev = i2c_dev_array[adap->nr];
- spin_unlock(&i2c_dev_array_lock);
+ spin_lock(&i2c_dev_list_lock);
+ list_for_each_entry(i2c_dev, &i2c_dev_list, list) {
+ if (i2c_dev->adap->nr == index)
+ goto found;
+ }
+ i2c_dev = NULL;
+found:
+ spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
@@ -76,30 +68,28 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
+ if (adap->nr >= I2C_MINORS) {
+ printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
+ adap->nr);
+ return ERR_PTR(-ENODEV);
+ }
+
i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
+ i2c_dev->adap = adap;
- spin_lock(&i2c_dev_array_lock);
- if (i2c_dev_array[adap->nr]) {
- spin_unlock(&i2c_dev_array_lock);
- dev_err(&adap->dev, "i2c-dev already has a device assigned to this adapter\n");
- goto error;
- }
- i2c_dev->minor = adap->nr;
- i2c_dev_array[adap->nr] = i2c_dev;
- spin_unlock(&i2c_dev_array_lock);
+ spin_lock(&i2c_dev_list_lock);
+ list_add_tail(&i2c_dev->list, &i2c_dev_list);
+ spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
-error:
- kfree(i2c_dev);
- return ERR_PTR(-ENODEV);
}
static void return_i2c_dev(struct i2c_dev *i2c_dev)
{
- spin_lock(&i2c_dev_array_lock);
- i2c_dev_array[i2c_dev->minor] = NULL;
- spin_unlock(&i2c_dev_array_lock);
+ spin_lock(&i2c_dev_list_lock);
+ list_del(&i2c_dev->list);
+ spin_unlock(&i2c_dev_list_lock);
}
static ssize_t show_adapter_name(struct class_device *class_dev, char *buf)
@@ -375,12 +365,13 @@ static int i2cdev_open(struct inode *inode, struct file *file)
if (!adap)
return -ENODEV;
- client = kmalloc(sizeof(*client), GFP_KERNEL);
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
- memcpy(client, &i2cdev_client_template, sizeof(*client));
+ snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
+ client->driver = &i2cdev_driver;
/* registered with adapter, passed as client to user */
client->adapter = adap;
@@ -415,41 +406,47 @@ static struct class *i2c_dev_class;
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
- struct device *dev;
+ int res;
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
- pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
- adap->name, i2c_dev->minor);
-
/* register this i2c device with the driver core */
- i2c_dev->adap = adap;
- dev = &adap->dev;
i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL,
- MKDEV(I2C_MAJOR, i2c_dev->minor),
- dev, "i2c-%d", i2c_dev->minor);
- if (!i2c_dev->class_dev)
+ MKDEV(I2C_MAJOR, adap->nr),
+ &adap->dev, "i2c-%d",
+ adap->nr);
+ if (!i2c_dev->class_dev) {
+ res = -ENODEV;
goto error;
- class_device_create_file(i2c_dev->class_dev, &class_device_attr_name);
+ }
+ res = class_device_create_file(i2c_dev->class_dev, &class_device_attr_name);
+ if (res)
+ goto error_destroy;
+
+ pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
+ adap->name, adap->nr);
return 0;
+error_destroy:
+ class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
return_i2c_dev(i2c_dev);
kfree(i2c_dev);
- return -ENODEV;
+ return res;
}
static int i2cdev_detach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
- i2c_dev = i2c_dev_get_by_adapter(adap);
- if (!i2c_dev)
- return -ENODEV;
+ i2c_dev = i2c_dev_get_by_minor(adap->nr);
+ if (!i2c_dev) /* attach_adapter must have failed */
+ return 0;
+ class_device_remove_file(i2c_dev->class_dev, &class_device_attr_name);
return_i2c_dev(i2c_dev);
- class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, i2c_dev->minor));
+ class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
kfree(i2c_dev);
pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
@@ -471,12 +468,6 @@ static struct i2c_driver i2cdev_driver = {
.detach_client = i2cdev_detach_client,
};
-static struct i2c_client i2cdev_client_template = {
- .name = "I2C /dev entry",
- .addr = -1,
- .driver = &i2cdev_driver,
-};
-
static int __init i2c_dev_init(void)
{
int res;
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index defd4b4bd37..9c8468de1a7 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -1207,7 +1207,7 @@ int system_bus_clock (void)
EXPORT_SYMBOL(system_bus_clock);
-static int generic_ide_suspend(struct device *dev, pm_message_t state)
+static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
{
ide_drive_t *drive = dev->driver_data;
struct request rq;
@@ -1221,7 +1221,9 @@ static int generic_ide_suspend(struct device *dev, pm_message_t state)
rq.special = &args;
rq.end_io_data = &rqpm;
rqpm.pm_step = ide_pm_state_start_suspend;
- rqpm.pm_state = state.event;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
+ rqpm.pm_state = mesg.event;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c
index 71f27e955d8..c7854ea57b5 100644
--- a/drivers/ide/mips/au1xxx-ide.c
+++ b/drivers/ide/mips/au1xxx-ide.c
@@ -476,13 +476,13 @@ static int auide_dma_lostirq(ide_drive_t *drive)
return 0;
}
-static void auide_ddma_tx_callback(int irq, void *param, struct pt_regs *regs)
+static void auide_ddma_tx_callback(int irq, void *param)
{
_auide_hwif *ahwif = (_auide_hwif*)param;
ahwif->drive->waiting_for_dma = 0;
}
-static void auide_ddma_rx_callback(int irq, void *param, struct pt_regs *regs)
+static void auide_ddma_rx_callback(int irq, void *param)
{
_auide_hwif *ahwif = (_auide_hwif*)param;
ahwif->drive->waiting_for_dma = 0;
diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c
index a574de5f083..d55b938b1ae 100644
--- a/drivers/ide/pci/atiixp.c
+++ b/drivers/ide/pci/atiixp.c
@@ -318,6 +318,20 @@ static void __devinit init_hwif_atiixp(ide_hwif_t *hwif)
hwif->drives[0].autodma = hwif->autodma;
}
+static void __devinit init_hwif_sb600_legacy(ide_hwif_t *hwif)
+{
+
+ hwif->atapi_dma = 1;
+ hwif->ultra_mask = 0x7f;
+ hwif->mwdma_mask = 0x07;
+ hwif->swdma_mask = 0x07;
+
+ if (!noautodma)
+ hwif->autodma = 1;
+ hwif->drives[0].autodma = hwif->autodma;
+ hwif->drives[1].autodma = hwif->autodma;
+}
+
static ide_pci_device_t atiixp_pci_info[] __devinitdata = {
{ /* 0 */
.name = "ATIIXP",
@@ -326,6 +340,12 @@ static ide_pci_device_t atiixp_pci_info[] __devinitdata = {
.autodma = AUTODMA,
.enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}},
.bootable = ON_BOARD,
+ },{ /* 1 */
+ .name = "ATI SB600 SATA Legacy IDE",
+ .init_hwif = init_hwif_sb600_legacy,
+ .channels = 2,
+ .autodma = AUTODMA,
+ .bootable = ON_BOARD,
}
};
@@ -348,6 +368,7 @@ static struct pci_device_id atiixp_pci_tbl[] = {
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, PCI_ANY_ID, PCI_ANY_ID, (PCI_CLASS_STORAGE_IDE<<8)|0x8a, 0xffff05, 1},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl);
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index 996c694341b..31ad79f52df 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -1369,15 +1369,16 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
}
static int
-pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t state)
+pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
{
ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
int rc = 0;
- if (state.event != mdev->ofdev.dev.power.power_state.event && state.event >= PM_EVENT_SUSPEND) {
+ if (mesg.event != mdev->ofdev.dev.power.power_state.event
+ && mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif);
if (rc == 0)
- mdev->ofdev.dev.power.power_state = state;
+ mdev->ofdev.dev.power.power_state = mesg;
}
return rc;
@@ -1473,15 +1474,16 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
}
static int
-pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
int rc = 0;
- if (state.event != pdev->dev.power.power_state.event && state.event >= 2) {
+ if (mesg.event != pdev->dev.power.power_state.event
+ && mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif);
if (rc == 0)
- pdev->dev.power.power_state = state;
+ pdev->dev.power.power_state = mesg;
}
return rc;
diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig
index 186737539cf..2769e505f05 100644
--- a/drivers/ieee1394/Kconfig
+++ b/drivers/ieee1394/Kconfig
@@ -120,12 +120,19 @@ config IEEE1394_VIDEO1394
this option only if you have an IEEE 1394 video device connected to
an OHCI-1394 card.
+comment "SBP-2 support (for storage devices) requires SCSI"
+ depends on IEEE1394 && SCSI=n
+
config IEEE1394_SBP2
tristate "SBP-2 support (Harddisks etc.)"
depends on IEEE1394 && SCSI && (PCI || BROKEN)
help
- This option enables you to use SBP-2 devices connected to your IEEE
- 1394 bus. SBP-2 devices include harddrives and DVD devices.
+ This option enables you to use SBP-2 devices connected to an IEEE
+ 1394 bus. SBP-2 devices include storage devices like harddisks and
+ DVD drives, also some other FireWire devices like scanners.
+
+ You should also enable support for disks, CD-ROMs, etc. in the SCSI
+ configuration section.
config IEEE1394_SBP2_PHYS_DMA
bool "Enable replacement for physical DMA in SBP2"
diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c
index 149573db91c..ab0c80f61b9 100644
--- a/drivers/ieee1394/csr.c
+++ b/drivers/ieee1394/csr.c
@@ -17,11 +17,13 @@
*
*/
-#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/param.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include "csr1212.h"
#include "ieee1394_types.h"
@@ -149,31 +151,18 @@ static void host_reset(struct hpsb_host *host)
/*
* HI == seconds (bits 0:2)
- * LO == fraction units of 1/8000 of a second, as per 1394 (bits 19:31)
- *
- * Convert to units and then to HZ, for comparison to jiffies.
- *
- * By default this will end up being 800 units, or 100ms (125usec per
- * unit).
+ * LO == fractions of a second in units of 125usec (bits 19:31)
*
- * NOTE: The spec says 1/8000, but also says we can compute based on 1/8192
- * like CSR specifies. Should make our math less complex.
+ * Convert SPLIT_TIMEOUT to jiffies.
+ * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms.
*/
static inline void calculate_expire(struct csr_control *csr)
{
- unsigned long units;
-
- /* Take the seconds, and convert to units */
- units = (unsigned long)(csr->split_timeout_hi & 0x07) << 13;
-
- /* Add in the fractional units */
- units += (unsigned long)(csr->split_timeout_lo >> 19);
-
- /* Convert to jiffies */
- csr->expire = (unsigned long)(units * HZ) >> 13UL;
+ unsigned long usecs =
+ (csr->split_timeout_hi & 0x07) * USEC_PER_SEC +
+ (csr->split_timeout_lo >> 19) * 125L;
- /* Just to keep from rounding low */
- csr->expire++;
+ csr->expire = usecs_to_jiffies(usecs > 100000L ? usecs : 100000L);
HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ);
}
diff --git a/drivers/ieee1394/csr.h b/drivers/ieee1394/csr.h
index ea9aa4f53ab..f11546550d8 100644
--- a/drivers/ieee1394/csr.h
+++ b/drivers/ieee1394/csr.h
@@ -1,75 +1,73 @@
-
#ifndef _IEEE1394_CSR_H
#define _IEEE1394_CSR_H
-#ifdef CONFIG_PREEMPT
-#include <linux/sched.h>
-#endif
+#include <linux/spinlock_types.h>
#include "csr1212.h"
+#include "ieee1394_types.h"
-#define CSR_REGISTER_BASE 0xfffff0000000ULL
+#define CSR_REGISTER_BASE 0xfffff0000000ULL
/* register offsets relative to CSR_REGISTER_BASE */
-#define CSR_STATE_CLEAR 0x0
-#define CSR_STATE_SET 0x4
-#define CSR_NODE_IDS 0x8
-#define CSR_RESET_START 0xc
-#define CSR_SPLIT_TIMEOUT_HI 0x18
-#define CSR_SPLIT_TIMEOUT_LO 0x1c
-#define CSR_CYCLE_TIME 0x200
-#define CSR_BUS_TIME 0x204
-#define CSR_BUSY_TIMEOUT 0x210
-#define CSR_BUS_MANAGER_ID 0x21c
-#define CSR_BANDWIDTH_AVAILABLE 0x220
-#define CSR_CHANNELS_AVAILABLE 0x224
-#define CSR_CHANNELS_AVAILABLE_HI 0x224
-#define CSR_CHANNELS_AVAILABLE_LO 0x228
-#define CSR_BROADCAST_CHANNEL 0x234
-#define CSR_CONFIG_ROM 0x400
-#define CSR_CONFIG_ROM_END 0x800
-#define CSR_FCP_COMMAND 0xB00
-#define CSR_FCP_RESPONSE 0xD00
-#define CSR_FCP_END 0xF00
-#define CSR_TOPOLOGY_MAP 0x1000
-#define CSR_TOPOLOGY_MAP_END 0x1400
-#define CSR_SPEED_MAP 0x2000
-#define CSR_SPEED_MAP_END 0x3000
+#define CSR_STATE_CLEAR 0x0
+#define CSR_STATE_SET 0x4
+#define CSR_NODE_IDS 0x8
+#define CSR_RESET_START 0xc
+#define CSR_SPLIT_TIMEOUT_HI 0x18
+#define CSR_SPLIT_TIMEOUT_LO 0x1c
+#define CSR_CYCLE_TIME 0x200
+#define CSR_BUS_TIME 0x204
+#define CSR_BUSY_TIMEOUT 0x210
+#define CSR_BUS_MANAGER_ID 0x21c
+#define CSR_BANDWIDTH_AVAILABLE 0x220
+#define CSR_CHANNELS_AVAILABLE 0x224
+#define CSR_CHANNELS_AVAILABLE_HI 0x224
+#define CSR_CHANNELS_AVAILABLE_LO 0x228
+#define CSR_BROADCAST_CHANNEL 0x234
+#define CSR_CONFIG_ROM 0x400
+#define CSR_CONFIG_ROM_END 0x800
+#define CSR_FCP_COMMAND 0xB00
+#define CSR_FCP_RESPONSE 0xD00
+#define CSR_FCP_END 0xF00
+#define CSR_TOPOLOGY_MAP 0x1000
+#define CSR_TOPOLOGY_MAP_END 0x1400
+#define CSR_SPEED_MAP 0x2000
+#define CSR_SPEED_MAP_END 0x3000
/* IEEE 1394 bus specific Configuration ROM Key IDs */
#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30)
-/* IEEE 1394 Bus Inforamation Block specifics */
+/* IEEE 1394 Bus Information Block specifics */
#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t))
-#define CSR_IRMC_SHIFT 31
-#define CSR_CMC_SHIFT 30
-#define CSR_ISC_SHIFT 29
-#define CSR_BMC_SHIFT 28
-#define CSR_PMC_SHIFT 27
-#define CSR_CYC_CLK_ACC_SHIFT 16
-#define CSR_MAX_REC_SHIFT 12
-#define CSR_MAX_ROM_SHIFT 8
-#define CSR_GENERATION_SHIFT 4
+#define CSR_IRMC_SHIFT 31
+#define CSR_CMC_SHIFT 30
+#define CSR_ISC_SHIFT 29
+#define CSR_BMC_SHIFT 28
+#define CSR_PMC_SHIFT 27
+#define CSR_CYC_CLK_ACC_SHIFT 16
+#define CSR_MAX_REC_SHIFT 12
+#define CSR_MAX_ROM_SHIFT 8
+#define CSR_GENERATION_SHIFT 4
#define CSR_SET_BUS_INFO_GENERATION(csr, gen) \
((csr)->bus_info_data[2] = \
cpu_to_be32((be32_to_cpu((csr)->bus_info_data[2]) & \
- ~(0xf << CSR_GENERATION_SHIFT)) | \
+ ~(0xf << CSR_GENERATION_SHIFT)) | \
(gen) << CSR_GENERATION_SHIFT))
struct csr_control {
- spinlock_t lock;
-
- quadlet_t state;
- quadlet_t node_ids;
- quadlet_t split_timeout_hi, split_timeout_lo;
- unsigned long expire; // Calculated from split_timeout
- quadlet_t cycle_time;
- quadlet_t bus_time;
- quadlet_t bus_manager_id;
- quadlet_t bandwidth_available;
- quadlet_t channels_available_hi, channels_available_lo;
+ spinlock_t lock;
+
+ quadlet_t state;
+ quadlet_t node_ids;
+ quadlet_t split_timeout_hi, split_timeout_lo;
+ unsigned long expire; /* Calculated from split_timeout */
+ quadlet_t cycle_time;
+ quadlet_t bus_time;
+ quadlet_t bus_manager_id;
+ quadlet_t bandwidth_available;
+ quadlet_t channels_available_hi, channels_available_lo;
quadlet_t broadcast_channel;
/* Bus Info */
@@ -84,8 +82,8 @@ struct csr_control {
struct csr1212_csr *rom;
- quadlet_t topology_map[256];
- quadlet_t speed_map[1024];
+ quadlet_t topology_map[256];
+ quadlet_t speed_map[1024];
};
extern struct csr1212_bus_ops csr_bus_ops;
@@ -93,4 +91,9 @@ extern struct csr1212_bus_ops csr_bus_ops;
int init_csr(void);
void cleanup_csr(void);
+/* hpsb_update_config_rom() is deprecated */
+struct hpsb_host;
+int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
+ size_t size, unsigned char rom_version);
+
#endif /* _IEEE1394_CSR_H */
diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c
index ca5167de707..c68f328e1a2 100644
--- a/drivers/ieee1394/dma.c
+++ b/drivers/ieee1394/dma.c
@@ -7,10 +7,13 @@
* directory of the kernel sources for details.
*/
+#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/vmalloc.h>
+#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <asm/scatterlist.h>
+
#include "dma.h"
/* dma_prog_region */
diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h
index 061550a6fb9..a1682aba71c 100644
--- a/drivers/ieee1394/dma.h
+++ b/drivers/ieee1394/dma.h
@@ -10,69 +10,91 @@
#ifndef IEEE1394_DMA_H
#define IEEE1394_DMA_H
-#include <linux/pci.h>
-#include <asm/scatterlist.h>
-
-/* struct dma_prog_region
-
- a small, physically-contiguous DMA buffer with random-access,
- synchronous usage characteristics
-*/
-
+#include <asm/types.h>
+
+struct pci_dev;
+struct scatterlist;
+struct vm_area_struct;
+
+/**
+ * struct dma_prog_region - small contiguous DMA buffer
+ * @kvirt: kernel virtual address
+ * @dev: PCI device
+ * @n_pages: number of kernel pages
+ * @bus_addr: base bus address
+ *
+ * a small, physically contiguous DMA buffer with random-access, synchronous
+ * usage characteristics
+ */
struct dma_prog_region {
- unsigned char *kvirt; /* kernel virtual address */
- struct pci_dev *dev; /* PCI device */
- unsigned int n_pages; /* # of kernel pages */
- dma_addr_t bus_addr; /* base bus address */
+ unsigned char *kvirt;
+ struct pci_dev *dev;
+ unsigned int n_pages;
+ dma_addr_t bus_addr;
};
/* clear out all fields but do not allocate any memory */
void dma_prog_region_init(struct dma_prog_region *prog);
-int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, struct pci_dev *dev);
+int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
+ struct pci_dev *dev);
void dma_prog_region_free(struct dma_prog_region *prog);
-static inline dma_addr_t dma_prog_region_offset_to_bus(struct dma_prog_region *prog, unsigned long offset)
+static inline dma_addr_t dma_prog_region_offset_to_bus(
+ struct dma_prog_region *prog, unsigned long offset)
{
return prog->bus_addr + offset;
}
-/* struct dma_region
-
- a large, non-physically-contiguous DMA buffer with streaming,
- asynchronous usage characteristics
-*/
-
+/**
+ * struct dma_region - large non-contiguous DMA buffer
+ * @virt: kernel virtual address
+ * @dev: PCI device
+ * @n_pages: number of kernel pages
+ * @n_dma_pages: number of IOMMU pages
+ * @sglist: IOMMU mapping
+ * @direction: PCI_DMA_TODEVICE, etc.
+ *
+ * a large, non-physically-contiguous DMA buffer with streaming, asynchronous
+ * usage characteristics
+ */
struct dma_region {
- unsigned char *kvirt; /* kernel virtual address */
- struct pci_dev *dev; /* PCI device */
- unsigned int n_pages; /* # of kernel pages */
- unsigned int n_dma_pages; /* # of IOMMU pages */
- struct scatterlist *sglist; /* IOMMU mapping */
- int direction; /* PCI_DMA_TODEVICE, etc */
+ unsigned char *kvirt;
+ struct pci_dev *dev;
+ unsigned int n_pages;
+ unsigned int n_dma_pages;
+ struct scatterlist *sglist;
+ int direction;
};
/* clear out all fields but do not allocate anything */
void dma_region_init(struct dma_region *dma);
/* allocate the buffer and map it to the IOMMU */
-int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction);
+int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
+ struct pci_dev *dev, int direction);
/* unmap and free the buffer */
void dma_region_free(struct dma_region *dma);
/* sync the CPU's view of the buffer */
-void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len);
+void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
+ unsigned long len);
+
/* sync the IO bus' view of the buffer */
-void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len);
+void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
+ unsigned long len);
/* map the buffer into a user space process */
-int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma);
+int dma_region_mmap(struct dma_region *dma, struct file *file,
+ struct vm_area_struct *vma);
/* macro to index into a DMA region (or dma_prog_region) */
-#define dma_region_i(_dma, _type, _index) ( ((_type*) ((_dma)->kvirt)) + (_index) )
+#define dma_region_i(_dma, _type, _index) \
+ ( ((_type*) ((_dma)->kvirt)) + (_index) )
/* return the DMA bus address of the byte with the given offset
- relative to the beginning of the dma_region */
-dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset);
+ * relative to the beginning of the dma_region */
+dma_addr_t dma_region_offset_to_bus(struct dma_region *dma,
+ unsigned long offset);
#endif /* IEEE1394_DMA_H */
diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h
index 80b5ac7fe38..7d1d2845b42 100644
--- a/drivers/ieee1394/dv1394-private.h
+++ b/drivers/ieee1394/dv1394-private.h
@@ -460,7 +460,7 @@ struct video_card {
int dma_running;
/*
- 3) the sleeping semaphore 'sem' - this is used from process context only,
+ 3) the sleeping mutex 'mtx' - this is used from process context only,
to serialize various operations on the video_card. Even though only one
open() is allowed, we still need to prevent multiple threads of execution
from entering calls like read, write, ioctl, etc.
@@ -468,9 +468,9 @@ struct video_card {
I honestly can't think of a good reason to use dv1394 from several threads
at once, but we need to serialize anyway to prevent oopses =).
- NOTE: if you need both spinlock and sem, take sem first to avoid deadlock!
+ NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock!
*/
- struct semaphore sem;
+ struct mutex mtx;
/* people waiting for buffer space, please form a line here... */
wait_queue_head_t waitq;
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
index 87532dd4337..6c72f04b2b5 100644
--- a/drivers/ieee1394/dv1394.c
+++ b/drivers/ieee1394/dv1394.c
@@ -95,6 +95,7 @@
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/bitops.h>
#include <asm/byteorder.h>
#include <asm/atomic.h>
@@ -110,15 +111,15 @@
#include <linux/compat.h>
#include <linux/cdev.h>
+#include "dv1394.h"
+#include "dv1394-private.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_types.h"
#include "nodemgr.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "dv1394.h"
-#include "dv1394-private.h"
-
#include "ohci1394.h"
/* DEBUG LEVELS:
@@ -136,13 +137,13 @@
#if DV1394_DEBUG_LEVEL >= 2
#define irq_printk( args... ) printk( args )
#else
-#define irq_printk( args... )
+#define irq_printk( args... ) do {} while (0)
#endif
#if DV1394_DEBUG_LEVEL >= 1
#define debug_printk( args... ) printk( args)
#else
-#define debug_printk( args... )
+#define debug_printk( args... ) do {} while (0)
#endif
/* issue a dummy PCI read to force the preceding write
@@ -247,7 +248,7 @@ static void frame_delete(struct frame *f)
Frame_prepare() must be called OUTSIDE the video->spinlock.
However, frame_prepare() must still be serialized, so
- it should be called WITH the video->sem taken.
+ it should be called WITH the video->mtx taken.
*/
static void frame_prepare(struct video_card *video, unsigned int this_frame)
@@ -1271,7 +1272,7 @@ static int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
int retval = -EINVAL;
/* serialize mmap */
- down(&video->sem);
+ mutex_lock(&video->mtx);
if ( ! video_card_initialized(video) ) {
retval = do_dv1394_init_default(video);
@@ -1281,7 +1282,7 @@ static int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
retval = dma_region_mmap(&video->dv_buf, file, vma);
out:
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return retval;
}
@@ -1337,17 +1338,17 @@ static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t
/* serialize this to prevent multi-threaded mayhem */
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&video->sem))
+ if (!mutex_trylock(&video->mtx))
return -EAGAIN;
} else {
- if (down_interruptible(&video->sem))
+ if (mutex_lock_interruptible(&video->mtx))
return -ERESTARTSYS;
}
if ( !video_card_initialized(video) ) {
ret = do_dv1394_init_default(video);
if (ret) {
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
}
@@ -1418,7 +1419,7 @@ static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t
remove_wait_queue(&video->waitq, &wait);
set_current_state(TASK_RUNNING);
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
@@ -1434,17 +1435,17 @@ static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count
/* serialize this to prevent multi-threaded mayhem */
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&video->sem))
+ if (!mutex_trylock(&video->mtx))
return -EAGAIN;
} else {
- if (down_interruptible(&video->sem))
+ if (mutex_lock_interruptible(&video->mtx))
return -ERESTARTSYS;
}
if ( !video_card_initialized(video) ) {
ret = do_dv1394_init_default(video);
if (ret) {
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
video->continuity_counter = -1;
@@ -1526,7 +1527,7 @@ static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count
remove_wait_queue(&video->waitq, &wait);
set_current_state(TASK_RUNNING);
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
@@ -1547,12 +1548,12 @@ static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
/* serialize this to prevent multi-threaded mayhem */
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&video->sem)) {
+ if (!mutex_trylock(&video->mtx)) {
unlock_kernel();
return -EAGAIN;
}
} else {
- if (down_interruptible(&video->sem)) {
+ if (mutex_lock_interruptible(&video->mtx)) {
unlock_kernel();
return -ERESTARTSYS;
}
@@ -1778,7 +1779,7 @@ static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
out:
- up(&video->sem);
+ mutex_unlock(&video->mtx);
unlock_kernel();
return ret;
}
@@ -2253,7 +2254,7 @@ static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes
clear_bit(0, &video->open);
spin_lock_init(&video->spinlock);
video->dma_running = 0;
- init_MUTEX(&video->sem);
+ mutex_init(&video->mtx);
init_waitqueue_head(&video->waitq);
video->fasync = NULL;
diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c
index 2d5b57be98c..8a7b8fab623 100644
--- a/drivers/ieee1394/eth1394.c
+++ b/drivers/ieee1394/eth1394.c
@@ -64,19 +64,19 @@
#include <linux/ethtool.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
-#include <asm/semaphore.h>
#include <net/arp.h>
+#include "config_roms.h"
#include "csr1212.h"
-#include "ieee1394_types.h"
+#include "eth1394.h"
+#include "highlevel.h"
+#include "ieee1394.h"
#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_transactions.h"
-#include "ieee1394.h"
-#include "highlevel.h"
+#include "ieee1394_types.h"
#include "iso.h"
#include "nodemgr.h"
-#include "eth1394.h"
-#include "config_roms.h"
#define ETH1394_PRINT_G(level, fmt, args...) \
printk(level "%s: " fmt, driver_name, ## args)
diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h
index e119fb87e5b..50f2dd2c7e2 100644
--- a/drivers/ieee1394/highlevel.h
+++ b/drivers/ieee1394/highlevel.h
@@ -1,60 +1,61 @@
-
#ifndef IEEE1394_HIGHLEVEL_H
#define IEEE1394_HIGHLEVEL_H
+#include <linux/list.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
-struct hpsb_address_serve {
- struct list_head host_list; /* per host list */
+struct module;
- struct list_head hl_list; /* hpsb_highlevel list */
+#include "ieee1394_types.h"
- struct hpsb_address_ops *op;
+struct hpsb_host;
+/* internal to ieee1394 core */
+struct hpsb_address_serve {
+ struct list_head host_list; /* per host list */
+ struct list_head hl_list; /* hpsb_highlevel list */
+ struct hpsb_address_ops *op;
struct hpsb_host *host;
-
- /* first address handled and first address behind, quadlet aligned */
- u64 start, end;
+ u64 start; /* first address handled, quadlet aligned */
+ u64 end; /* first address behind, quadlet aligned */
};
-
-/*
- * The above structs are internal to highlevel driver handling. Only the
- * following structures are of interest to actual highlevel drivers.
- */
+/* Only the following structures are of interest to actual highlevel drivers. */
struct hpsb_highlevel {
struct module *owner;
const char *name;
- /* Any of the following pointers can legally be NULL, except for
- * iso_receive which can only be NULL when you don't request
- * channels. */
+ /* Any of the following pointers can legally be NULL, except for
+ * iso_receive which can only be NULL when you don't request
+ * channels. */
- /* New host initialized. Will also be called during
- * hpsb_register_highlevel for all hosts already installed. */
- void (*add_host) (struct hpsb_host *host);
+ /* New host initialized. Will also be called during
+ * hpsb_register_highlevel for all hosts already installed. */
+ void (*add_host)(struct hpsb_host *host);
- /* Host about to be removed. Will also be called during
- * hpsb_unregister_highlevel once for each host. */
- void (*remove_host) (struct hpsb_host *host);
+ /* Host about to be removed. Will also be called during
+ * hpsb_unregister_highlevel once for each host. */
+ void (*remove_host)(struct hpsb_host *host);
- /* Host experienced bus reset with possible configuration changes.
+ /* Host experienced bus reset with possible configuration changes.
* Note that this one may occur during interrupt/bottom half handling.
* You can not expect to be able to do stock hpsb_reads. */
- void (*host_reset) (struct hpsb_host *host);
+ void (*host_reset)(struct hpsb_host *host);
- /* An isochronous packet was received. Channel contains the channel
- * number for your convenience, it is also contained in the included
- * packet header (first quadlet, CRCs are missing). You may get called
- * for channel/host combinations you did not request. */
- void (*iso_receive) (struct hpsb_host *host, int channel,
- quadlet_t *data, size_t length);
+ /* An isochronous packet was received. Channel contains the channel
+ * number for your convenience, it is also contained in the included
+ * packet header (first quadlet, CRCs are missing). You may get called
+ * for channel/host combinations you did not request. */
+ void (*iso_receive)(struct hpsb_host *host, int channel,
+ quadlet_t *data, size_t length);
- /* A write request was received on either the FCP_COMMAND (direction =
- * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg
- * contains the cts field (first byte of data). */
- void (*fcp_request) (struct hpsb_host *host, int nodeid, int direction,
- int cts, u8 *data, size_t length);
+ /* A write request was received on either the FCP_COMMAND (direction =
+ * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg
+ * contains the cts field (first byte of data). */
+ void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction,
+ int cts, u8 *data, size_t length);
/* These are initialized by the subsystem when the
* hpsb_higlevel is registered. */
@@ -67,61 +68,62 @@ struct hpsb_highlevel {
};
struct hpsb_address_ops {
- /*
- * Null function pointers will make the respective operation complete
- * with RCODE_TYPE_ERROR. Makes for easy to implement read-only
- * registers (just leave everything but read NULL).
- *
- * All functions shall return appropriate IEEE 1394 rcodes.
- */
-
- /* These functions have to implement block reads for themselves. */
- /* These functions either return a response code
- or a negative number. In the first case a response will be generated; in the
- later case, no response will be sent and the driver, that handled the request
- will send the response itself
- */
- int (*read) (struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, size_t length, u16 flags);
- int (*write) (struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, size_t length, u16 flags);
-
- /* Lock transactions: write results of ext_tcode operation into
- * *store. */
- int (*lock) (struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags);
- int (*lock64) (struct hpsb_host *host, int nodeid, octlet_t *store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags);
+ /*
+ * Null function pointers will make the respective operation complete
+ * with RCODE_TYPE_ERROR. Makes for easy to implement read-only
+ * registers (just leave everything but read NULL).
+ *
+ * All functions shall return appropriate IEEE 1394 rcodes.
+ */
+
+ /* These functions have to implement block reads for themselves.
+ *
+ * These functions either return a response code or a negative number.
+ * In the first case a response will be generated. In the latter case,
+ * no response will be sent and the driver which handled the request
+ * will send the response itself. */
+ int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+ u64 addr, size_t length, u16 flags);
+ int (*write)(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t *data, u64 addr, size_t length, u16 flags);
+
+ /* Lock transactions: write results of ext_tcode operation into
+ * *store. */
+ int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
+ int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
};
-
void highlevel_add_host(struct hpsb_host *host);
void highlevel_remove_host(struct hpsb_host *host);
void highlevel_host_reset(struct hpsb_host *host);
-
-/* these functions are called to handle transactions. They are called, when
- a packet arrives. The flags argument contains the second word of the first header
- quadlet of the incoming packet (containing transaction label, retry code,
- transaction code and priority). These functions either return a response code
- or a negative number. In the first case a response will be generated; in the
- later case, no response will be sent and the driver, that handled the request
- will send the response itself.
-*/
-int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
- u64 addr, unsigned int length, u16 flags);
-int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
- void *data, u64 addr, unsigned int length, u16 flags);
+/*
+ * These functions are called to handle transactions. They are called when a
+ * packet arrives. The flags argument contains the second word of the first
+ * header quadlet of the incoming packet (containing transaction label, retry
+ * code, transaction code and priority). These functions either return a
+ * response code or a negative number. In the first case a response will be
+ * generated. In the latter case, no response will be sent and the driver which
+ * handled the request will send the response itself.
+ */
+int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
+ unsigned int length, u16 flags);
+int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
+ u64 addr, unsigned int length, u16 flags);
int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags);
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags);
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
-void highlevel_iso_receive(struct hpsb_host *host, void *data,
- size_t length);
+void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length);
void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
- void *data, size_t length);
-
+ void *data, size_t length);
/*
* Register highlevel driver. The name pointer has to stay valid at all times
@@ -132,13 +134,15 @@ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
/*
* Register handlers for host address spaces. Start and end are 48 bit pointers
- * and have to be quadlet aligned (end points to the first address behind the
- * handled addresses. This function can be called multiple times for a single
- * hpsb_highlevel to implement sparse register sets. The requested region must
- * not overlap any previously allocated region, otherwise registering will fail.
+ * and have to be quadlet aligned. Argument "end" points to the first address
+ * behind the handled addresses. This function can be called multiple times for
+ * a single hpsb_highlevel to implement sparse register sets. The requested
+ * region must not overlap any previously allocated region, otherwise
+ * registering will fail.
*
- * It returns true for successful allocation. There is no unregister function,
- * all address spaces are deallocated together with the hpsb_highlevel.
+ * It returns true for successful allocation. Address spaces can be
+ * unregistered with hpsb_unregister_addrspace. All remaining address spaces
+ * are automatically deallocated together with the hpsb_highlevel.
*/
u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
struct hpsb_host *host,
@@ -146,20 +150,18 @@ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
u64 size, u64 alignment,
u64 start, u64 end);
int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- struct hpsb_address_ops *ops, u64 start, u64 end);
-
+ struct hpsb_address_ops *ops, u64 start, u64 end);
int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- u64 start);
+ u64 start);
/*
* Enable or disable receving a certain isochronous channel through the
* iso_receive op.
*/
int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
- unsigned int channel);
+ unsigned int channel);
void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
- unsigned int channel);
-
+ unsigned int channel);
/* Retrieve a hostinfo pointer bound to this driver/host */
void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
@@ -172,19 +174,24 @@ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
/* Set an alternate lookup key for the hostinfo bound to this driver/host */
-void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key);
+void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ unsigned long key);
-/* Retrieve the alternate lookup key for the hostinfo bound to this driver/host */
-unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host);
+/* Retrieve the alternate lookup key for the hostinfo bound to this
+ * driver/host */
+unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl,
+ struct hpsb_host *host);
/* Retrieve a hostinfo pointer bound to this driver using its alternate key */
void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key);
/* Set the hostinfo pointer to something useful. Usually follows a call to
* hpsb_create_hostinfo, where the size is 0. */
-int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data);
+int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ void *data);
/* Retrieve hpsb_host using a highlevel handle and a key */
-struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, unsigned long key);
+struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl,
+ unsigned long key);
#endif /* IEEE1394_HIGHLEVEL_H */
diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c
index 4feead4a35c..d90a3a1898c 100644
--- a/drivers/ieee1394/hosts.c
+++ b/drivers/ieee1394/hosts.c
@@ -90,6 +90,16 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data)
return 0;
}
+/*
+ * The pending_packet_queue is special in that it's processed
+ * from hardirq context too (such as hpsb_bus_reset()). Hence
+ * split the lock class from the usual networking skb-head
+ * lock class by using a separate key for it:
+ */
+static struct lock_class_key pending_packet_queue_key;
+
+static DEFINE_MUTEX(host_num_alloc);
+
/**
* hpsb_alloc_host - allocate a new host controller.
* @drv: the driver that will manage the host controller
@@ -105,16 +115,6 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data)
* Return Value: a pointer to the &hpsb_host if successful, %NULL if
* no memory was available.
*/
-static DEFINE_MUTEX(host_num_alloc);
-
-/*
- * The pending_packet_queue is special in that it's processed
- * from hardirq context too (such as hpsb_bus_reset()). Hence
- * split the lock class from the usual networking skb-head
- * lock class by using a separate key for it:
- */
-static struct lock_class_key pending_packet_queue_key;
-
struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
struct device *dev)
{
@@ -143,9 +143,6 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
for (i = 2; i < 16; i++)
h->csr.gen_timestamp[i] = jiffies - 60 * HZ;
- for (i = 0; i < ARRAY_SIZE(h->tpool); i++)
- HPSB_TPOOL_INIT(&h->tpool[i]);
-
atomic_set(&h->generation, 0);
INIT_WORK(&h->delayed_reset, delayed_reset_bus, h);
diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h
index 9ad4b246307..bc6dbfadb89 100644
--- a/drivers/ieee1394/hosts.h
+++ b/drivers/ieee1394/hosts.h
@@ -2,17 +2,19 @@
#define _IEEE1394_HOSTS_H
#include <linux/device.h>
-#include <linux/wait.h>
#include <linux/list.h>
-#include <linux/timer.h>
#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <asm/atomic.h>
-#include <asm/semaphore.h>
+struct pci_dev;
+struct module;
#include "ieee1394_types.h"
#include "csr.h"
-
struct hpsb_packet;
struct hpsb_iso;
@@ -33,7 +35,6 @@ struct hpsb_host {
int node_count; /* number of identified nodes on this bus */
int selfid_count; /* total number of SelfIDs received */
int nodes_active; /* number of nodes with active link layer */
- u8 speed[ALL_NODES]; /* speed between each node and local node */
nodeid_t node_id; /* node ID of this host */
nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
@@ -53,31 +54,29 @@ struct hpsb_host {
int reset_retries;
quadlet_t *topology_map;
u8 *speed_map;
- struct csr_control csr;
-
- /* Per node tlabel pool allocation */
- struct hpsb_tlabel_pool tpool[ALL_NODES];
+ int id;
struct hpsb_host_driver *driver;
-
struct pci_dev *pdev;
-
- int id;
-
struct device device;
struct class_device class_dev;
int update_config_rom;
struct work_struct delayed_reset;
-
unsigned int config_roms;
struct list_head addr_space;
u64 low_addr_space; /* upper bound of physical DMA area */
u64 middle_addr_space; /* upper bound of posted write area */
-};
+ u8 speed[ALL_NODES]; /* speed between each node and local node */
+
+ /* per node tlabel allocation */
+ u8 next_tl[ALL_NODES];
+ struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES];
+ struct csr_control csr;
+};
enum devctl_cmd {
/* Host is requested to reset its bus and cancel all outstanding async
@@ -112,7 +111,7 @@ enum devctl_cmd {
enum isoctl_cmd {
/* rawiso API - see iso.h for the meanings of these commands
- (they correspond exactly to the hpsb_iso_* API functions)
+ * (they correspond exactly to the hpsb_iso_* API functions)
* INIT = allocate resources
* START = begin transmission/reception
* STOP = halt transmission/reception
@@ -160,7 +159,8 @@ struct hpsb_host_driver {
/* The hardware driver may optionally support a function that is used
* to set the hardware ConfigROM if the hardware supports handling
* reads to the ConfigROM on its own. */
- void (*set_hw_config_rom) (struct hpsb_host *host, quadlet_t *config_rom);
+ void (*set_hw_config_rom)(struct hpsb_host *host,
+ quadlet_t *config_rom);
/* This function shall implement packet transmission based on
* packet->type. It shall CRC both parts of the packet (unless
@@ -170,20 +170,21 @@ struct hpsb_host_driver {
* called. Return 0 on success, negative errno on failure.
* NOTE: The function must be callable in interrupt context.
*/
- int (*transmit_packet) (struct hpsb_host *host,
- struct hpsb_packet *packet);
+ int (*transmit_packet)(struct hpsb_host *host,
+ struct hpsb_packet *packet);
/* This function requests miscellanous services from the driver, see
* above for command codes and expected actions. Return -1 for unknown
* command, though that should never happen.
*/
- int (*devctl) (struct hpsb_host *host, enum devctl_cmd command, int arg);
+ int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg);
/* ISO transmission/reception functions. Return 0 on success, -1
* (or -EXXX errno code) on failure. If the low-level driver does not
* support the new ISO API, set isoctl to NULL.
*/
- int (*isoctl) (struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg);
+ int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command,
+ unsigned long arg);
/* This function is mainly to redirect local CSR reads/locks to the iso
* management registers (bus manager id, bandwidth available, channels
@@ -196,19 +197,11 @@ struct hpsb_host_driver {
quadlet_t data, quadlet_t compare);
};
-
struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
struct device *dev);
int hpsb_add_host(struct hpsb_host *host);
void hpsb_remove_host(struct hpsb_host *h);
-/* The following 2 functions are deprecated and will be removed when the
- * raw1394/libraw1394 update is complete. */
-int hpsb_update_config_rom(struct hpsb_host *host,
- const quadlet_t *new_rom, size_t size, unsigned char rom_version);
-int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer,
- size_t buffersize, size_t *rom_size, unsigned char *rom_version);
-
/* Updates the configuration rom image of a host. rom_version must be the
* current version, otherwise it will fail with return value -1. If this
* host does not support config-rom-update, it will return -EINVAL.
diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h
index 15670398634..8f207508ed1 100644
--- a/drivers/ieee1394/ieee1394-ioctl.h
+++ b/drivers/ieee1394/ieee1394-ioctl.h
@@ -1,5 +1,7 @@
-/* Base file for all ieee1394 ioctl's. Linux-1394 has allocated base '#'
- * with a range of 0x00-0x3f. */
+/*
+ * Base file for all ieee1394 ioctl's.
+ * Linux-1394 has allocated base '#' with a range of 0x00-0x3f.
+ */
#ifndef __IEEE1394_IOCTL_H
#define __IEEE1394_IOCTL_H
@@ -96,8 +98,7 @@
_IOW ('#', 0x27, struct raw1394_iso_packets)
#define RAW1394_IOC_ISO_XMIT_SYNC \
_IO ('#', 0x28)
-#define RAW1394_IOC_ISO_RECV_FLUSH \
+#define RAW1394_IOC_ISO_RECV_FLUSH \
_IO ('#', 0x29)
-
#endif /* __IEEE1394_IOCTL_H */
diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h
index 936d776de00..40492074c01 100644
--- a/drivers/ieee1394/ieee1394.h
+++ b/drivers/ieee1394/ieee1394.h
@@ -5,77 +5,78 @@
#ifndef _IEEE1394_IEEE1394_H
#define _IEEE1394_IEEE1394_H
-#define TCODE_WRITEQ 0x0
-#define TCODE_WRITEB 0x1
-#define TCODE_WRITE_RESPONSE 0x2
-#define TCODE_READQ 0x4
-#define TCODE_READB 0x5
-#define TCODE_READQ_RESPONSE 0x6
-#define TCODE_READB_RESPONSE 0x7
-#define TCODE_CYCLE_START 0x8
-#define TCODE_LOCK_REQUEST 0x9
-#define TCODE_ISO_DATA 0xa
-#define TCODE_STREAM_DATA 0xa
-#define TCODE_LOCK_RESPONSE 0xb
-
-#define RCODE_COMPLETE 0x0
-#define RCODE_CONFLICT_ERROR 0x4
-#define RCODE_DATA_ERROR 0x5
-#define RCODE_TYPE_ERROR 0x6
-#define RCODE_ADDRESS_ERROR 0x7
-
-#define EXTCODE_MASK_SWAP 0x1
-#define EXTCODE_COMPARE_SWAP 0x2
-#define EXTCODE_FETCH_ADD 0x3
-#define EXTCODE_LITTLE_ADD 0x4
-#define EXTCODE_BOUNDED_ADD 0x5
-#define EXTCODE_WRAP_ADD 0x6
-
-#define ACK_COMPLETE 0x1
-#define ACK_PENDING 0x2
-#define ACK_BUSY_X 0x4
-#define ACK_BUSY_A 0x5
-#define ACK_BUSY_B 0x6
-#define ACK_TARDY 0xb
-#define ACK_CONFLICT_ERROR 0xc
-#define ACK_DATA_ERROR 0xd
-#define ACK_TYPE_ERROR 0xe
-#define ACK_ADDRESS_ERROR 0xf
+#define TCODE_WRITEQ 0x0
+#define TCODE_WRITEB 0x1
+#define TCODE_WRITE_RESPONSE 0x2
+#define TCODE_READQ 0x4
+#define TCODE_READB 0x5
+#define TCODE_READQ_RESPONSE 0x6
+#define TCODE_READB_RESPONSE 0x7
+#define TCODE_CYCLE_START 0x8
+#define TCODE_LOCK_REQUEST 0x9
+#define TCODE_ISO_DATA 0xa
+#define TCODE_STREAM_DATA 0xa
+#define TCODE_LOCK_RESPONSE 0xb
+
+#define RCODE_COMPLETE 0x0
+#define RCODE_CONFLICT_ERROR 0x4
+#define RCODE_DATA_ERROR 0x5
+#define RCODE_TYPE_ERROR 0x6
+#define RCODE_ADDRESS_ERROR 0x7
+
+#define EXTCODE_MASK_SWAP 0x1
+#define EXTCODE_COMPARE_SWAP 0x2
+#define EXTCODE_FETCH_ADD 0x3
+#define EXTCODE_LITTLE_ADD 0x4
+#define EXTCODE_BOUNDED_ADD 0x5
+#define EXTCODE_WRAP_ADD 0x6
+
+#define ACK_COMPLETE 0x1
+#define ACK_PENDING 0x2
+#define ACK_BUSY_X 0x4
+#define ACK_BUSY_A 0x5
+#define ACK_BUSY_B 0x6
+#define ACK_TARDY 0xb
+#define ACK_CONFLICT_ERROR 0xc
+#define ACK_DATA_ERROR 0xd
+#define ACK_TYPE_ERROR 0xe
+#define ACK_ADDRESS_ERROR 0xf
/* Non-standard "ACK codes" for internal use */
-#define ACKX_NONE (-1)
-#define ACKX_SEND_ERROR (-2)
-#define ACKX_ABORTED (-3)
-#define ACKX_TIMEOUT (-4)
-
-
-#define IEEE1394_SPEED_100 0x00
-#define IEEE1394_SPEED_200 0x01
-#define IEEE1394_SPEED_400 0x02
-#define IEEE1394_SPEED_800 0x03
-#define IEEE1394_SPEED_1600 0x04
-#define IEEE1394_SPEED_3200 0x05
+#define ACKX_NONE (-1)
+#define ACKX_SEND_ERROR (-2)
+#define ACKX_ABORTED (-3)
+#define ACKX_TIMEOUT (-4)
+
+#define IEEE1394_SPEED_100 0x00
+#define IEEE1394_SPEED_200 0x01
+#define IEEE1394_SPEED_400 0x02
+#define IEEE1394_SPEED_800 0x03
+#define IEEE1394_SPEED_1600 0x04
+#define IEEE1394_SPEED_3200 0x05
+
/* The current highest tested speed supported by the subsystem */
-#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800
+#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800
/* Maps speed values above to a string representation */
extern const char *hpsb_speedto_str[];
-
/* 1394a cable PHY packets */
-#define SELFID_PWRCL_NO_POWER 0x0
-#define SELFID_PWRCL_PROVIDE_15W 0x1
-#define SELFID_PWRCL_PROVIDE_30W 0x2
-#define SELFID_PWRCL_PROVIDE_45W 0x3
-#define SELFID_PWRCL_USE_1W 0x4
-#define SELFID_PWRCL_USE_3W 0x5
-#define SELFID_PWRCL_USE_6W 0x6
-#define SELFID_PWRCL_USE_10W 0x7
-
-#define SELFID_PORT_CHILD 0x3
-#define SELFID_PORT_PARENT 0x2
-#define SELFID_PORT_NCONN 0x1
-#define SELFID_PORT_NONE 0x0
+#define SELFID_PWRCL_NO_POWER 0x0
+#define SELFID_PWRCL_PROVIDE_15W 0x1
+#define SELFID_PWRCL_PROVIDE_30W 0x2
+#define SELFID_PWRCL_PROVIDE_45W 0x3
+#define SELFID_PWRCL_USE_1W 0x4
+#define SELFID_PWRCL_USE_3W 0x5
+#define SELFID_PWRCL_USE_6W 0x6
+#define SELFID_PWRCL_USE_10W 0x7
+
+#define SELFID_PORT_CHILD 0x3
+#define SELFID_PORT_PARENT 0x2
+#define SELFID_PORT_NCONN 0x1
+#define SELFID_PORT_NONE 0x0
+
+#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */
#define PHYPACKET_LINKON 0x40000000
#define PHYPACKET_PHYCONFIG_R 0x00800000
@@ -91,76 +92,76 @@ extern const char *hpsb_speedto_str[];
#define EXTPHYPACKET_TYPEMASK 0xC0FC0000
-#define PHYPACKET_PORT_SHIFT 24
-#define PHYPACKET_GAPCOUNT_SHIFT 16
+#define PHYPACKET_PORT_SHIFT 24
+#define PHYPACKET_GAPCOUNT_SHIFT 16
/* 1394a PHY register map bitmasks */
-#define PHY_00_PHYSICAL_ID 0xFC
-#define PHY_00_R 0x02 /* Root */
-#define PHY_00_PS 0x01 /* Power Status*/
-#define PHY_01_RHB 0x80 /* Root Hold-Off */
-#define PHY_01_IBR 0x80 /* Initiate Bus Reset */
-#define PHY_01_GAP_COUNT 0x3F
-#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */
-#define PHY_02_TOTAL_PORTS 0x1F
-#define PHY_03_MAX_SPEED 0xE0
-#define PHY_03_DELAY 0x0F
-#define PHY_04_LCTRL 0x80 /* Link Active Report Control */
-#define PHY_04_CONTENDER 0x40
-#define PHY_04_JITTER 0x38
-#define PHY_04_PWR_CLASS 0x07 /* Power Class */
-#define PHY_05_WATCHDOG 0x80
-#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */
-#define PHY_05_LOOP 0x20 /* Loop Detect */
-#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */
-#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */
-#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */
-#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */
-#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */
+#define PHY_00_PHYSICAL_ID 0xFC
+#define PHY_00_R 0x02 /* Root */
+#define PHY_00_PS 0x01 /* Power Status*/
+#define PHY_01_RHB 0x80 /* Root Hold-Off */
+#define PHY_01_IBR 0x80 /* Initiate Bus Reset */
+#define PHY_01_GAP_COUNT 0x3F
+#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */
+#define PHY_02_TOTAL_PORTS 0x1F
+#define PHY_03_MAX_SPEED 0xE0
+#define PHY_03_DELAY 0x0F
+#define PHY_04_LCTRL 0x80 /* Link Active Report Control */
+#define PHY_04_CONTENDER 0x40
+#define PHY_04_JITTER 0x38
+#define PHY_04_PWR_CLASS 0x07 /* Power Class */
+#define PHY_05_WATCHDOG 0x80
+#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */
+#define PHY_05_LOOP 0x20 /* Loop Detect */
+#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */
+#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */
+#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */
+#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */
+#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */
#include <asm/byteorder.h>
#ifdef __BIG_ENDIAN_BITFIELD
struct selfid {
- u32 packet_identifier:2; /* always binary 10 */
- u32 phy_id:6;
- /* byte */
- u32 extended:1; /* if true is struct ext_selfid */
- u32 link_active:1;
- u32 gap_count:6;
- /* byte */
- u32 speed:2;
- u32 phy_delay:2;
- u32 contender:1;
- u32 power_class:3;
- /* byte */
- u32 port0:2;
- u32 port1:2;
- u32 port2:2;
- u32 initiated_reset:1;
- u32 more_packets:1;
+ u32 packet_identifier:2; /* always binary 10 */
+ u32 phy_id:6;
+ /* byte */
+ u32 extended:1; /* if true is struct ext_selfid */
+ u32 link_active:1;
+ u32 gap_count:6;
+ /* byte */
+ u32 speed:2;
+ u32 phy_delay:2;
+ u32 contender:1;
+ u32 power_class:3;
+ /* byte */
+ u32 port0:2;
+ u32 port1:2;
+ u32 port2:2;
+ u32 initiated_reset:1;
+ u32 more_packets:1;
} __attribute__((packed));
struct ext_selfid {
- u32 packet_identifier:2; /* always binary 10 */
- u32 phy_id:6;
- /* byte */
- u32 extended:1; /* if false is struct selfid */
- u32 seq_nr:3;
- u32 reserved:2;
- u32 porta:2;
- /* byte */
- u32 portb:2;
- u32 portc:2;
- u32 portd:2;
- u32 porte:2;
- /* byte */
- u32 portf:2;
- u32 portg:2;
- u32 porth:2;
- u32 reserved2:1;
- u32 more_packets:1;
+ u32 packet_identifier:2; /* always binary 10 */
+ u32 phy_id:6;
+ /* byte */
+ u32 extended:1; /* if false is struct selfid */
+ u32 seq_nr:3;
+ u32 reserved:2;
+ u32 porta:2;
+ /* byte */
+ u32 portb:2;
+ u32 portc:2;
+ u32 portd:2;
+ u32 porte:2;
+ /* byte */
+ u32 portf:2;
+ u32 portg:2;
+ u32 porth:2;
+ u32 reserved2:1;
+ u32 more_packets:1;
} __attribute__((packed));
#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */
@@ -171,49 +172,48 @@ struct ext_selfid {
*/
struct selfid {
- u32 phy_id:6;
- u32 packet_identifier:2; /* always binary 10 */
- /* byte */
- u32 gap_count:6;
- u32 link_active:1;
- u32 extended:1; /* if true is struct ext_selfid */
- /* byte */
- u32 power_class:3;
- u32 contender:1;
- u32 phy_delay:2;
- u32 speed:2;
- /* byte */
- u32 more_packets:1;
- u32 initiated_reset:1;
- u32 port2:2;
- u32 port1:2;
- u32 port0:2;
+ u32 phy_id:6;
+ u32 packet_identifier:2; /* always binary 10 */
+ /* byte */
+ u32 gap_count:6;
+ u32 link_active:1;
+ u32 extended:1; /* if true is struct ext_selfid */
+ /* byte */
+ u32 power_class:3;
+ u32 contender:1;
+ u32 phy_delay:2;
+ u32 speed:2;
+ /* byte */
+ u32 more_packets:1;
+ u32 initiated_reset:1;
+ u32 port2:2;
+ u32 port1:2;
+ u32 port0:2;
} __attribute__((packed));
struct ext_selfid {
- u32 phy_id:6;
- u32 packet_identifier:2; /* always binary 10 */
- /* byte */
- u32 porta:2;
- u32 reserved:2;
- u32 seq_nr:3;
- u32 extended:1; /* if false is struct selfid */
- /* byte */
- u32 porte:2;
- u32 portd:2;
- u32 portc:2;
- u32 portb:2;
- /* byte */
- u32 more_packets:1;
- u32 reserved2:1;
- u32 porth:2;
- u32 portg:2;
- u32 portf:2;
+ u32 phy_id:6;
+ u32 packet_identifier:2; /* always binary 10 */
+ /* byte */
+ u32 porta:2;
+ u32 reserved:2;
+ u32 seq_nr:3;
+ u32 extended:1; /* if false is struct selfid */
+ /* byte */
+ u32 porte:2;
+ u32 portd:2;
+ u32 portc:2;
+ u32 portb:2;
+ /* byte */
+ u32 more_packets:1;
+ u32 reserved2:1;
+ u32 porth:2;
+ u32 portg:2;
+ u32 portf:2;
} __attribute__((packed));
#else
#error What? PDP endian?
#endif /* __BIG_ENDIAN_BITFIELD */
-
#endif /* _IEEE1394_IEEE1394_H */
diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c
index f43739c5cab..5fccf9f7a1d 100644
--- a/drivers/ieee1394/ieee1394_core.c
+++ b/drivers/ieee1394/ieee1394_core.c
@@ -35,7 +35,6 @@
#include <linux/kthread.h>
#include <asm/byteorder.h>
-#include <asm/semaphore.h>
#include "ieee1394_types.h"
#include "ieee1394.h"
@@ -86,7 +85,7 @@ static void dump_packet(const char *text, quadlet_t *data, int size, int speed)
printk("\n");
}
#else
-#define dump_packet(a,b,c,d)
+#define dump_packet(a,b,c,d) do {} while (0)
#endif
static void abort_requests(struct hpsb_host *host);
@@ -355,10 +354,12 @@ static void build_speed_map(struct hpsb_host *host, int nodecount)
}
}
+#if SELFID_SPEED_UNKNOWN != IEEE1394_SPEED_MAX
/* assume maximum speed for 1394b PHYs, nodemgr will correct it */
for (n = 0; n < nodecount; n++)
- if (speedcap[n] == 3)
+ if (speedcap[n] == SELFID_SPEED_UNKNOWN)
speedcap[n] = IEEE1394_SPEED_MAX;
+#endif
}
@@ -1169,7 +1170,7 @@ static void __exit ieee1394_cleanup(void)
unregister_chrdev_region(IEEE1394_CORE_DEV, 256);
}
-module_init(ieee1394_init);
+fs_initcall(ieee1394_init); /* same as ohci1394 */
module_exit(ieee1394_cleanup);
/* Exported symbols */
diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
index 0ecbf335c64..af4a78a8ef3 100644
--- a/drivers/ieee1394/ieee1394_core.h
+++ b/drivers/ieee1394/ieee1394_core.h
@@ -1,12 +1,15 @@
-
#ifndef _IEEE1394_CORE_H
#define _IEEE1394_CORE_H
-#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
#include <asm/atomic.h>
-#include <asm/semaphore.h>
-#include "hosts.h"
+#include "hosts.h"
+#include "ieee1394_types.h"
struct hpsb_packet {
/* This struct is basically read-only for hosts with the exception of
@@ -58,7 +61,6 @@ struct hpsb_packet {
size_t header_size;
size_t data_size;
-
struct hpsb_host *host;
unsigned int generation;
@@ -80,7 +82,7 @@ struct hpsb_packet {
/* Set a task for when a packet completes */
void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
- void (*routine)(void *), void *data);
+ void (*routine)(void *), void *data);
static inline struct hpsb_packet *driver_packet(struct list_head *l)
{
@@ -92,7 +94,6 @@ void abort_timedouts(unsigned long __opaque);
struct hpsb_packet *hpsb_alloc_packet(size_t data_size);
void hpsb_free_packet(struct hpsb_packet *packet);
-
/*
* Generation counter for the complete 1394 subsystem. Generation gets
* incremented on every change in the subsystem (e.g. bus reset).
@@ -204,10 +205,14 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15
#define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0)
-#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16)
-#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_VIDEO1394 * 16)
-#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16)
-#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16)
+#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_RAW1394 * 16)
+#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_VIDEO1394 * 16)
+#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_DV1394 * 16)
+#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16)
/* return the index (within a minor number block) of a file */
static inline unsigned char ieee1394_file_to_instance(struct file *file)
@@ -223,4 +228,3 @@ extern struct class hpsb_host_class;
extern struct class *hpsb_protocol_class;
#endif /* _IEEE1394_CORE_H */
-
diff --git a/drivers/ieee1394/ieee1394_hotplug.h b/drivers/ieee1394/ieee1394_hotplug.h
index 5be70d31b00..dd5500ed832 100644
--- a/drivers/ieee1394/ieee1394_hotplug.h
+++ b/drivers/ieee1394/ieee1394_hotplug.h
@@ -1,33 +1,19 @@
#ifndef _IEEE1394_HOTPLUG_H
#define _IEEE1394_HOTPLUG_H
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/mod_devicetable.h>
-
/* Unit spec id and sw version entry for some protocols */
#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define AVC_SW_VERSION_ENTRY 0x00010001
#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define CAMERA_SW_VERSION_ENTRY 0x00000100
-/* Check to make sure this all isn't already defined */
-#ifndef IEEE1394_MATCH_VENDOR_ID
-
-#define IEEE1394_MATCH_VENDOR_ID 0x0001
-#define IEEE1394_MATCH_MODEL_ID 0x0002
-#define IEEE1394_MATCH_SPECIFIER_ID 0x0004
-#define IEEE1394_MATCH_VERSION 0x0008
-
-struct ieee1394_device_id {
- u32 match_flags;
- u32 vendor_id;
- u32 model_id;
- u32 specifier_id;
- u32 version;
- void *driver_data;
-};
-
-#endif
+/* /include/linux/mod_devicetable.h defines:
+ * IEEE1394_MATCH_VENDOR_ID
+ * IEEE1394_MATCH_MODEL_ID
+ * IEEE1394_MATCH_SPECIFIER_ID
+ * IEEE1394_MATCH_VERSION
+ * struct ieee1394_device_id
+ */
+#include <linux/mod_devicetable.h>
#endif /* _IEEE1394_HOTPLUG_H */
diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c
index a114b91d606..0833fc9f50c 100644
--- a/drivers/ieee1394/ieee1394_transactions.c
+++ b/drivers/ieee1394/ieee1394_transactions.c
@@ -9,19 +9,17 @@
* directory of the kernel sources for details.
*/
-#include <linux/sched.h>
#include <linux/bitops.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <asm/bug.h>
#include <asm/errno.h>
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "nodemgr.h"
#include "ieee1394_transactions.h"
#define PREP_ASYNC_HEAD_ADDRESS(tc) \
@@ -31,6 +29,13 @@
packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \
packet->header[2] = addr & 0xffffffff
+#ifndef HPSB_DEBUG_TLABELS
+static
+#endif
+spinlock_t hpsb_tlabel_lock = SPIN_LOCK_UNLOCKED;
+
+static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq);
+
static void fill_async_readquad(struct hpsb_packet *packet, u64 addr)
{
PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ);
@@ -114,9 +119,41 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length,
packet->tcode = TCODE_ISO_DATA;
}
+/* same as hpsb_get_tlabel, except that it returns immediately */
+static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet)
+{
+ unsigned long flags, *tp;
+ u8 *next;
+ int tlabel, n = NODEID_TO_NODE(packet->node_id);
+
+ /* Broadcast transactions are complete once the request has been sent.
+ * Use the same transaction label for all broadcast transactions. */
+ if (unlikely(n == ALL_NODES)) {
+ packet->tlabel = 0;
+ return 0;
+ }
+ tp = packet->host->tl_pool[n].map;
+ next = &packet->host->next_tl[n];
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ tlabel = find_next_zero_bit(tp, 64, *next);
+ if (tlabel > 63)
+ tlabel = find_first_zero_bit(tp, 64);
+ if (tlabel > 63) {
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+ return -EAGAIN;
+ }
+ __set_bit(tlabel, tp);
+ *next = (tlabel + 1) & 63;
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ packet->tlabel = tlabel;
+ return 0;
+}
+
/**
* hpsb_get_tlabel - allocate a transaction label
- * @packet: the packet who's tlabel/tpool we set
+ * @packet: the packet whose tlabel and tl_pool we set
*
* Every asynchronous transaction on the 1394 bus needs a transaction
* label to match the response to the request. This label has to be
@@ -130,42 +167,25 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length,
* Return value: Zero on success, otherwise non-zero. A non-zero return
* generally means there are no available tlabels. If this is called out
* of interrupt or atomic context, then it will sleep until can return a
- * tlabel.
+ * tlabel or a signal is received.
*/
int hpsb_get_tlabel(struct hpsb_packet *packet)
{
- unsigned long flags;
- struct hpsb_tlabel_pool *tp;
- int n = NODEID_TO_NODE(packet->node_id);
-
- if (unlikely(n == ALL_NODES))
- return 0;
- tp = &packet->host->tpool[n];
-
- if (irqs_disabled() || in_atomic()) {
- if (down_trylock(&tp->count))
- return 1;
- } else {
- down(&tp->count);
- }
-
- spin_lock_irqsave(&tp->lock, flags);
-
- packet->tlabel = find_next_zero_bit(tp->pool, 64, tp->next);
- if (packet->tlabel > 63)
- packet->tlabel = find_first_zero_bit(tp->pool, 64);
- tp->next = (packet->tlabel + 1) % 64;
- /* Should _never_ happen */
- BUG_ON(test_and_set_bit(packet->tlabel, tp->pool));
- tp->allocations++;
- spin_unlock_irqrestore(&tp->lock, flags);
-
- return 0;
+ if (irqs_disabled() || in_atomic())
+ return hpsb_get_tlabel_atomic(packet);
+
+ /* NB: The macro wait_event_interruptible() is called with a condition
+ * argument with side effect. This is only possible because the side
+ * effect does not occur until the condition became true, and
+ * wait_event_interruptible() won't evaluate the condition again after
+ * that. */
+ return wait_event_interruptible(tlabel_wq,
+ !hpsb_get_tlabel_atomic(packet));
}
/**
* hpsb_free_tlabel - free an allocated transaction label
- * @packet: packet whos tlabel/tpool needs to be cleared
+ * @packet: packet whose tlabel and tl_pool needs to be cleared
*
* Frees the transaction label allocated with hpsb_get_tlabel(). The
* tlabel has to be freed after the transaction is complete (i.e. response
@@ -176,21 +196,20 @@ int hpsb_get_tlabel(struct hpsb_packet *packet)
*/
void hpsb_free_tlabel(struct hpsb_packet *packet)
{
- unsigned long flags;
- struct hpsb_tlabel_pool *tp;
- int n = NODEID_TO_NODE(packet->node_id);
+ unsigned long flags, *tp;
+ int tlabel, n = NODEID_TO_NODE(packet->node_id);
if (unlikely(n == ALL_NODES))
return;
- tp = &packet->host->tpool[n];
+ tp = packet->host->tl_pool[n].map;
+ tlabel = packet->tlabel;
+ BUG_ON(tlabel > 63 || tlabel < 0);
- BUG_ON(packet->tlabel > 63 || packet->tlabel < 0);
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ BUG_ON(!__test_and_clear_bit(tlabel, tp));
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
- spin_lock_irqsave(&tp->lock, flags);
- BUG_ON(!test_and_clear_bit(packet->tlabel, tp->pool));
- spin_unlock_irqrestore(&tp->lock, flags);
-
- up(&tp->count);
+ wake_up_interruptible(&tlabel_wq);
}
int hpsb_packet_success(struct hpsb_packet *packet)
@@ -214,7 +233,7 @@ int hpsb_packet_success(struct hpsb_packet *packet)
packet->node_id);
return -EAGAIN;
}
- HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__);
+ BUG();
case ACK_BUSY_X:
case ACK_BUSY_A:
@@ -261,8 +280,7 @@ int hpsb_packet_success(struct hpsb_packet *packet)
packet->ack_code, packet->node_id, packet->tcode);
return -EAGAIN;
}
-
- HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__);
+ BUG();
}
struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h
index 45ba784fe6d..c1369c41469 100644
--- a/drivers/ieee1394/ieee1394_transactions.h
+++ b/drivers/ieee1394/ieee1394_transactions.h
@@ -1,32 +1,32 @@
#ifndef _IEEE1394_TRANSACTIONS_H
#define _IEEE1394_TRANSACTIONS_H
-#include "ieee1394_core.h"
+#include <linux/types.h>
+#include "ieee1394_types.h"
+
+struct hpsb_packet;
+struct hpsb_host;
-/*
- * Get and free transaction labels.
- */
int hpsb_get_tlabel(struct hpsb_packet *packet);
void hpsb_free_tlabel(struct hpsb_packet *packet);
-
struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
u64 addr, size_t length);
struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node,
- u64 addr, int extcode, quadlet_t *data,
+ u64 addr, int extcode, quadlet_t *data,
quadlet_t arg);
-struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, nodeid_t node,
- u64 addr, int extcode, octlet_t *data,
- octlet_t arg);
-struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host,
- quadlet_t data) ;
-struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host,
- int length, int channel,
- int tag, int sync);
-struct hpsb_packet *hpsb_make_writepacket (struct hpsb_host *host, nodeid_t node,
- u64 addr, quadlet_t *buffer, size_t length);
+struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host,
+ nodeid_t node, u64 addr, int extcode,
+ octlet_t *data, octlet_t arg);
+struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data);
+struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, int length,
+ int channel, int tag, int sync);
+struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host,
+ nodeid_t node, u64 addr,
+ quadlet_t *buffer, size_t length);
struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer,
- int length, int channel, int tag, int sync);
+ int length, int channel, int tag,
+ int sync);
/*
* hpsb_packet_success - Make sense of the ack and reply codes and
@@ -40,9 +40,8 @@ struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer,
*/
int hpsb_packet_success(struct hpsb_packet *packet);
-
/*
- * The generic read, write and lock functions. All recognize the local node ID
+ * The generic read and write functions. All recognize the local node ID
* and act accordingly. Read and write automatically use quadlet commands if
* length == 4 and and block commands otherwise (however, they do not yet
* support lengths that are not a multiple of 4). You must explicitly specifiy
@@ -54,4 +53,8 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
u64 addr, quadlet_t *buffer, size_t length);
+#ifdef HPSB_DEBUG_TLABELS
+extern spinlock_t hpsb_tlabel_lock;
+#endif
+
#endif /* _IEEE1394_TRANSACTIONS_H */
diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h
index 3165609ec1e..9803aaa15be 100644
--- a/drivers/ieee1394/ieee1394_types.h
+++ b/drivers/ieee1394/ieee1394_types.h
@@ -1,37 +1,11 @@
-
#ifndef _IEEE1394_TYPES_H
#define _IEEE1394_TYPES_H
#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
#include <linux/string.h>
-
-#include <asm/semaphore.h>
+#include <linux/types.h>
#include <asm/byteorder.h>
-
-/* Transaction Label handling */
-struct hpsb_tlabel_pool {
- DECLARE_BITMAP(pool, 64);
- spinlock_t lock;
- u8 next;
- u32 allocations;
- struct semaphore count;
-};
-
-#define HPSB_TPOOL_INIT(_tp) \
-do { \
- bitmap_zero((_tp)->pool, 64); \
- spin_lock_init(&(_tp)->lock); \
- (_tp)->next = 0; \
- (_tp)->allocations = 0; \
- sema_init(&(_tp)->count, 63); \
-} while (0)
-
-
typedef u32 quadlet_t;
typedef u64 octlet_t;
typedef u16 nodeid_t;
@@ -54,46 +28,40 @@ typedef u16 arm_length_t;
#define NODE_BUS_ARGS(__host, __nodeid) \
__host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid)
-#define HPSB_PRINT(level, fmt, args...) printk(level "ieee1394: " fmt "\n" , ## args)
+#define HPSB_PRINT(level, fmt, args...) \
+ printk(level "ieee1394: " fmt "\n" , ## args)
-#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
-#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args)
-#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args)
-#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args)
-#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args)
+#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args)
+#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args)
+#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args)
+#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args)
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_DEBUG_TLABELS
#else
-#define HPSB_VERBOSE(fmt, args...)
+#define HPSB_VERBOSE(fmt, args...) do {} while (0)
#endif
-#define HPSB_PANIC(fmt, args...) panic("ieee1394: " fmt "\n" , ## args)
-
-#define HPSB_TRACE() HPSB_PRINT(KERN_INFO, "TRACE - %s, %s(), line %d", __FILE__, __FUNCTION__, __LINE__)
-
-
#ifdef __BIG_ENDIAN
-static __inline__ void *memcpy_le32(u32 *dest, const u32 *__src, size_t count)
+static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count)
{
- void *tmp = dest;
+ void *tmp = dest;
u32 *src = (u32 *)__src;
- count /= 4;
-
- while (count--) {
- *dest++ = swab32p(src++);
- }
-
- return tmp;
+ count /= 4;
+ while (count--)
+ *dest++ = swab32p(src++);
+ return tmp;
}
#else
static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count)
{
- return memcpy(dest, src, count);
+ return memcpy(dest, src, count);
}
#endif /* __BIG_ENDIAN */
diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c
index f26680ebef7..08bd15d2a7b 100644
--- a/drivers/ieee1394/iso.c
+++ b/drivers/ieee1394/iso.c
@@ -9,8 +9,11 @@
* directory of the kernel sources for details.
*/
-#include <linux/slab.h>
+#include <linux/pci.h>
#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "hosts.h"
#include "iso.h"
void hpsb_iso_stop(struct hpsb_iso *iso)
diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h
index 3efc60b33a8..1210a97e868 100644
--- a/drivers/ieee1394/iso.h
+++ b/drivers/ieee1394/iso.h
@@ -12,33 +12,40 @@
#ifndef IEEE1394_ISO_H
#define IEEE1394_ISO_H
-#include "hosts.h"
+#include <linux/spinlock_types.h>
+#include <asm/atomic.h>
+#include <asm/types.h>
+
#include "dma.h"
-/* high-level ISO interface */
+struct hpsb_host;
-/* This API sends and receives isochronous packets on a large,
- virtually-contiguous kernel memory buffer. The buffer may be mapped
- into a user-space process for zero-copy transmission and reception.
+/* high-level ISO interface */
- There are no explicit boundaries between packets in the buffer. A
- packet may be transmitted or received at any location. However,
- low-level drivers may impose certain restrictions on alignment or
- size of packets. (e.g. in OHCI no packet may cross a page boundary,
- and packets should be quadlet-aligned)
-*/
+/*
+ * This API sends and receives isochronous packets on a large,
+ * virtually-contiguous kernel memory buffer. The buffer may be mapped
+ * into a user-space process for zero-copy transmission and reception.
+ *
+ * There are no explicit boundaries between packets in the buffer. A
+ * packet may be transmitted or received at any location. However,
+ * low-level drivers may impose certain restrictions on alignment or
+ * size of packets. (e.g. in OHCI no packet may cross a page boundary,
+ * and packets should be quadlet-aligned)
+ */
/* Packet descriptor - the API maintains a ring buffer of these packet
- descriptors in kernel memory (hpsb_iso.infos[]). */
-
+ * descriptors in kernel memory (hpsb_iso.infos[]). */
struct hpsb_iso_packet_info {
/* offset of data payload relative to the first byte of the buffer */
__u32 offset;
- /* length of the data payload, in bytes (not including the isochronous header) */
+ /* length of the data payload, in bytes (not including the isochronous
+ * header) */
__u16 len;
- /* (recv only) the cycle number (mod 8000) on which the packet was received */
+ /* (recv only) the cycle number (mod 8000) on which the packet was
+ * received */
__u16 cycle;
/* (recv only) channel on which the packet was received */
@@ -48,12 +55,10 @@ struct hpsb_iso_packet_info {
__u8 tag;
__u8 sy;
- /*
- * length in bytes of the packet including header/trailer.
- * MUST be at structure end, since the first part of this structure is also
- * defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is copied to
- * userspace and is accessed there through libraw1394.
- */
+ /* length in bytes of the packet including header/trailer.
+ * MUST be at structure end, since the first part of this structure is
+ * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is
+ * copied to userspace and is accessed there through libraw1394. */
__u16 total_len;
};
@@ -75,8 +80,8 @@ struct hpsb_iso {
void *hostdata;
/* a function to be called (from interrupt context) after
- outgoing packets have been sent, or incoming packets have
- arrived */
+ * outgoing packets have been sent, or incoming packets have
+ * arrived */
void (*callback)(struct hpsb_iso*);
/* wait for buffer space */
@@ -88,7 +93,7 @@ struct hpsb_iso {
/* greatest # of packets between interrupts - controls
- the maximum latency of the buffer */
+ * the maximum latency of the buffer */
int irq_interval;
/* the buffer for packet data payloads */
@@ -112,8 +117,8 @@ struct hpsb_iso {
int pkt_dma;
/* how many packets, starting at first_packet:
- (transmit) are ready to be filled with data
- (receive) contain received data */
+ * (transmit) are ready to be filled with data
+ * (receive) contain received data */
int n_ready_packets;
/* how many times the buffer has overflowed or underflowed */
@@ -134,7 +139,7 @@ struct hpsb_iso {
int start_cycle;
/* cycle at which next packet will be transmitted,
- -1 if not known */
+ * -1 if not known */
int xmit_cycle;
/* ringbuffer of packet descriptors in regular kernel memory
@@ -170,25 +175,30 @@ int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel);
int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask);
/* start/stop DMA */
-int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, int prebuffer);
-int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, int tag_mask, int sync);
+int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle,
+ int prebuffer);
+int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle,
+ int tag_mask, int sync);
void hpsb_iso_stop(struct hpsb_iso *iso);
/* deallocate buffer and DMA context */
void hpsb_iso_shutdown(struct hpsb_iso *iso);
-/* queue a packet for transmission. 'offset' is relative to the beginning of the
- DMA buffer, where the packet's data payload should already have been placed */
-int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy);
+/* queue a packet for transmission.
+ * 'offset' is relative to the beginning of the DMA buffer, where the packet's
+ * data payload should already have been placed. */
+int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
+ u8 tag, u8 sy);
/* wait until all queued packets have been transmitted to the bus */
int hpsb_iso_xmit_sync(struct hpsb_iso *iso);
/* N packets have been read out of the buffer, re-use the buffer space */
-int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, unsigned int n_packets);
+int hpsb_iso_recv_release_packets(struct hpsb_iso *recv,
+ unsigned int n_packets);
/* check for arrival of new packets immediately (even if irq_interval
- has not yet been reached) */
+ * has not yet been reached) */
int hpsb_iso_recv_flush(struct hpsb_iso *iso);
/* returns # of packets ready to send or receive */
@@ -197,14 +207,15 @@ int hpsb_iso_n_ready(struct hpsb_iso *iso);
/* the following are callbacks available to low-level drivers */
/* call after a packet has been transmitted to the bus (interrupt context is OK)
- 'cycle' is the _exact_ cycle the packet was sent on
- 'error' should be non-zero if some sort of error occurred when sending the packet
-*/
+ * 'cycle' is the _exact_ cycle the packet was sent on
+ * 'error' should be non-zero if some sort of error occurred when sending the
+ * packet */
void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error);
/* call after a packet has been received (interrupt context OK) */
void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
- u16 total_len, u16 cycle, u8 channel, u8 tag, u8 sy);
+ u16 total_len, u16 cycle, u8 channel, u8 tag,
+ u8 sy);
/* call to wake waiting processes after buffer space has opened up. */
void hpsb_iso_wake(struct hpsb_iso *iso);
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index d541b508a15..3e7974c5744 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -12,26 +12,23 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
-#include <linux/kmod.h>
-#include <linux/completion.h>
#include <linux/delay.h>
-#include <linux/pci.h>
+#include <linux/kthread.h>
#include <linux/moduleparam.h>
#include <asm/atomic.h>
-#include "ieee1394_types.h"
+#include "csr.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
#include "ieee1394_core.h"
-#include "hosts.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
#include "ieee1394_transactions.h"
-#include "highlevel.h"
-#include "csr.h"
#include "nodemgr.h"
static int ignore_drivers;
-module_param(ignore_drivers, int, 0444);
+module_param(ignore_drivers, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers.");
struct nodemgr_csr_info {
@@ -71,7 +68,7 @@ static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr,
u8 i, *speed, old_speed, good_speed;
int ret;
- speed = ci->host->speed + NODEID_TO_NODE(ci->nodeid);
+ speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]);
old_speed = *speed;
good_speed = IEEE1394_SPEED_MAX + 1;
@@ -161,16 +158,12 @@ static struct csr1212_bus_ops nodemgr_csr_ops = {
* but now we are much simpler because of the LDM.
*/
-static DECLARE_MUTEX(nodemgr_serialize);
+static DEFINE_MUTEX(nodemgr_serialize);
struct host_info {
struct hpsb_host *host;
struct list_head list;
- struct completion exited;
- struct semaphore reset_sem;
- int pid;
- char daemon_name[15];
- int kill_me;
+ struct task_struct *thread;
};
static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
@@ -334,34 +327,44 @@ static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribut
static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
-/* tlabels_free, tlabels_allocations, tlabels_mask are read non-atomically
- * here, therefore displayed values may be occasionally wrong. */
-static ssize_t fw_show_ne_tlabels_free(struct device *dev, struct device_attribute *attr, char *buf)
+#ifdef HPSB_DEBUG_TLABELS
+static ssize_t fw_show_ne_tlabels_free(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
- return sprintf(buf, "%d\n", 64 - bitmap_weight(ne->tpool->pool, 64));
-}
-static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ int tf;
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ tf = 64 - bitmap_weight(tp, 64);
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
-static ssize_t fw_show_ne_tlabels_allocations(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct node_entry *ne = container_of(dev, struct node_entry, device);
- return sprintf(buf, "%u\n", ne->tpool->allocations);
+ return sprintf(buf, "%d\n", tf);
}
-static DEVICE_ATTR(tlabels_allocations,S_IRUGO,fw_show_ne_tlabels_allocations,NULL);
+static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
-static ssize_t fw_show_ne_tlabels_mask(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t fw_show_ne_tlabels_mask(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ u64 tm;
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
#if (BITS_PER_LONG <= 32)
- return sprintf(buf, "0x%08lx%08lx\n", ne->tpool->pool[0], ne->tpool->pool[1]);
+ tm = ((u64)tp[0] << 32) + tp[1];
#else
- return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]);
+ tm = tp[0];
#endif
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ return sprintf(buf, "0x%016llx\n", tm);
}
static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
+#endif /* HPSB_DEBUG_TLABELS */
static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
@@ -408,26 +411,11 @@ static ssize_t fw_get_destroy_node(struct bus_type *bus, char *buf)
}
static BUS_ATTR(destroy_node, S_IWUSR | S_IRUGO, fw_get_destroy_node, fw_set_destroy_node);
-static int nodemgr_rescan_bus_thread(void *__unused)
-{
- /* No userlevel access needed */
- daemonize("kfwrescan");
-
- bus_rescan_devices(&ieee1394_bus_type);
-
- return 0;
-}
static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, size_t count)
{
- int state = simple_strtoul(buf, NULL, 10);
-
- /* Don't wait for this, or care about errors. Root could do
- * something stupid and spawn this a lot of times, but that's
- * root's fault. */
- if (state == 1)
- kernel_thread(nodemgr_rescan_bus_thread, NULL, CLONE_KERNEL);
-
+ if (simple_strtoul(buf, NULL, 10) == 1)
+ bus_rescan_devices(&ieee1394_bus_type);
return count;
}
static ssize_t fw_get_rescan(struct bus_type *bus, char *buf)
@@ -483,9 +471,10 @@ static struct device_attribute *const fw_ne_attrs[] = {
&dev_attr_ne_vendor_id,
&dev_attr_ne_nodeid,
&dev_attr_bus_options,
+#ifdef HPSB_DEBUG_TLABELS
&dev_attr_tlabels_free,
- &dev_attr_tlabels_allocations,
&dev_attr_tlabels_mask,
+#endif
};
@@ -804,8 +793,6 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, struct csr1212_csr
if (!ne)
return NULL;
- ne->tpool = &host->tpool[nodeid & NODE_MASK];
-
ne->host = host;
ne->nodeid = nodeid;
ne->generation = generation;
@@ -1251,6 +1238,7 @@ static void nodemgr_node_scan_one(struct host_info *hi,
octlet_t guid;
struct csr1212_csr *csr;
struct nodemgr_csr_info *ci;
+ u8 *speed;
ci = kmalloc(sizeof(*ci), GFP_KERNEL);
if (!ci)
@@ -1259,8 +1247,12 @@ static void nodemgr_node_scan_one(struct host_info *hi,
ci->host = host;
ci->nodeid = nodeid;
ci->generation = generation;
- ci->speed_unverified =
- host->speed[NODEID_TO_NODE(nodeid)] > IEEE1394_SPEED_100;
+
+ /* Prepare for speed probe which occurs when reading the ROM */
+ speed = &(host->speed[NODEID_TO_NODE(nodeid)]);
+ if (*speed > host->csr.lnk_spd)
+ *speed = host->csr.lnk_spd;
+ ci->speed_unverified = *speed > IEEE1394_SPEED_100;
/* We need to detect when the ConfigROM's generation has changed,
* so we only update the node's info when it needs to be. */
@@ -1300,8 +1292,6 @@ static void nodemgr_node_scan_one(struct host_info *hi,
nodemgr_create_node(guid, csr, hi, nodeid, generation);
else
nodemgr_update_node(ne, csr, hi, nodeid, generation);
-
- return;
}
@@ -1326,6 +1316,7 @@ static void nodemgr_node_scan(struct host_info *hi, int generation)
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
static void nodemgr_suspend_ne(struct node_entry *ne)
{
struct class_device *cdev;
@@ -1361,6 +1352,7 @@ static void nodemgr_resume_ne(struct node_entry *ne)
ne->in_limbo = 0;
device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
+ down_read(&nodemgr_ud_class.subsys.rwsem);
down_read(&ne->device.bus->subsys.rwsem);
list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
ud = container_of(cdev, struct unit_directory, class_dev);
@@ -1372,21 +1364,21 @@ static void nodemgr_resume_ne(struct node_entry *ne)
ud->device.driver->resume(&ud->device);
}
up_read(&ne->device.bus->subsys.rwsem);
+ up_read(&nodemgr_ud_class.subsys.rwsem);
HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
static void nodemgr_update_pdrv(struct node_entry *ne)
{
struct unit_directory *ud;
struct hpsb_protocol_driver *pdrv;
- struct class *class = &nodemgr_ud_class;
struct class_device *cdev;
- down_read(&class->subsys.rwsem);
- list_for_each_entry(cdev, &class->children, node) {
+ list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
ud = container_of(cdev, struct unit_directory, class_dev);
if (ud->ne != ne || !ud->device.driver)
continue;
@@ -1399,7 +1391,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne)
up_write(&ud->device.bus->subsys.rwsem);
}
}
- up_read(&class->subsys.rwsem);
}
@@ -1430,6 +1421,8 @@ static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader because the
+ * calls to nodemgr_update_pdrv() and nodemgr_suspend_ne() here require it. */
static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation)
{
struct device *dev;
@@ -1492,9 +1485,8 @@ static void nodemgr_node_probe(struct host_info *hi, int generation)
/* If we had a bus reset while we were scanning the bus, it is
* possible that we did not probe all nodes. In that case, we
* skip the clean up for now, since we could remove nodes that
- * were still on the bus. The bus reset increased hi->reset_sem,
- * so there's a bus scan pending which will do the clean up
- * eventually.
+ * were still on the bus. Another bus scan is pending which will
+ * do the clean up eventually.
*
* Now let's tell the bus to rescan our devices. This may seem
* like overhead, but the driver-model core will only scan a
@@ -1622,41 +1614,37 @@ static int nodemgr_host_thread(void *__hi)
{
struct host_info *hi = (struct host_info *)__hi;
struct hpsb_host *host = hi->host;
- int reset_cycles = 0;
-
- /* No userlevel access needed */
- daemonize(hi->daemon_name);
+ unsigned int g, generation = get_hpsb_generation(host) - 1;
+ int i, reset_cycles = 0;
/* Setup our device-model entries */
nodemgr_create_host_dev_files(host);
- /* Sit and wait for a signal to probe the nodes on the bus. This
- * happens when we get a bus reset. */
- while (1) {
- unsigned int generation = 0;
- int i;
+ for (;;) {
+ /* Sleep until next bus reset */
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (get_hpsb_generation(host) == generation)
+ schedule();
+ __set_current_state(TASK_RUNNING);
+
+ /* Thread may have been woken up to freeze or to exit */
+ if (try_to_freeze())
+ continue;
+ if (kthread_should_stop())
+ goto exit;
- if (down_interruptible(&hi->reset_sem) ||
- down_interruptible(&nodemgr_serialize)) {
+ if (mutex_lock_interruptible(&nodemgr_serialize)) {
if (try_to_freeze())
continue;
- printk("NodeMgr: received unexpected signal?!\n" );
- break;
- }
-
- if (hi->kill_me) {
- up(&nodemgr_serialize);
- break;
+ goto exit;
}
/* Pause for 1/4 second in 1/16 second intervals,
* to make sure things settle down. */
+ g = get_hpsb_generation(host);
for (i = 0; i < 4 ; i++) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (msleep_interruptible(63)) {
- up(&nodemgr_serialize);
- goto caught_signal;
- }
+ if (msleep_interruptible(63) || kthread_should_stop())
+ goto unlock_exit;
/* Now get the generation in which the node ID's we collect
* are valid. During the bus scan we will use this generation
@@ -1667,20 +1655,14 @@ static int nodemgr_host_thread(void *__hi)
/* If we get a reset before we are done waiting, then
* start the the waiting over again */
- while (!down_trylock(&hi->reset_sem))
- i = 0;
-
- /* Check the kill_me again */
- if (hi->kill_me) {
- up(&nodemgr_serialize);
- goto caught_signal;
- }
+ if (generation != g)
+ g = generation, i = 0;
}
if (!nodemgr_check_irm_capability(host, reset_cycles) ||
!nodemgr_do_irm_duties(host, reset_cycles)) {
reset_cycles++;
- up(&nodemgr_serialize);
+ mutex_unlock(&nodemgr_serialize);
continue;
}
reset_cycles = 0;
@@ -1698,13 +1680,13 @@ static int nodemgr_host_thread(void *__hi)
/* Update some of our sysfs symlinks */
nodemgr_update_host_dev_links(host);
- up(&nodemgr_serialize);
+ mutex_unlock(&nodemgr_serialize);
}
-
-caught_signal:
+unlock_exit:
+ mutex_unlock(&nodemgr_serialize);
+exit:
HPSB_VERBOSE("NodeMgr: Exiting thread");
-
- complete_and_exit(&hi->exited, 0);
+ return 0;
}
int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
@@ -1764,41 +1746,27 @@ static void nodemgr_add_host(struct hpsb_host *host)
struct host_info *hi;
hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi));
-
if (!hi) {
- HPSB_ERR ("NodeMgr: out of memory in add host");
+ HPSB_ERR("NodeMgr: out of memory in add host");
return;
}
-
hi->host = host;
- init_completion(&hi->exited);
- sema_init(&hi->reset_sem, 0);
-
- sprintf(hi->daemon_name, "knodemgrd_%d", host->id);
-
- hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_KERNEL);
-
- if (hi->pid < 0) {
- HPSB_ERR ("NodeMgr: failed to start %s thread for %s",
- hi->daemon_name, host->driver->name);
+ hi->thread = kthread_run(nodemgr_host_thread, hi, "knodemgrd_%d",
+ host->id);
+ if (IS_ERR(hi->thread)) {
+ HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id);
hpsb_destroy_hostinfo(&nodemgr_highlevel, host);
- return;
}
-
- return;
}
static void nodemgr_host_reset(struct hpsb_host *host)
{
struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
- if (hi != NULL) {
- HPSB_VERBOSE("NodeMgr: Processing host reset for %s", hi->daemon_name);
- up(&hi->reset_sem);
- } else
- HPSB_ERR ("NodeMgr: could not process reset of unused host");
-
- return;
+ if (hi) {
+ HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id);
+ wake_up_process(hi->thread);
+ }
}
static void nodemgr_remove_host(struct hpsb_host *host)
@@ -1806,18 +1774,9 @@ static void nodemgr_remove_host(struct hpsb_host *host)
struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
if (hi) {
- if (hi->pid >= 0) {
- hi->kill_me = 1;
- mb();
- up(&hi->reset_sem);
- wait_for_completion(&hi->exited);
- nodemgr_remove_host_dev(&host->device);
- }
- } else
- HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
- host->driver->name);
-
- return;
+ kthread_stop(hi->thread);
+ nodemgr_remove_host_dev(&host->device);
+ }
}
static struct hpsb_highlevel nodemgr_highlevel = {
diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h
index 0b26616e16c..0e1e7d93078 100644
--- a/drivers/ieee1394/nodemgr.h
+++ b/drivers/ieee1394/nodemgr.h
@@ -21,9 +21,15 @@
#define _IEEE1394_NODEMGR_H
#include <linux/device.h>
-#include "csr1212.h"
+#include <asm/types.h>
+
#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
+
+struct csr1212_csr;
+struct csr1212_keyval;
+struct hpsb_host;
+struct ieee1394_device_id;
/* '1' '3' '9' '4' in ASCII */
#define IEEE1394_BUSID_MAGIC __constant_cpu_to_be32(0x31333934)
@@ -44,7 +50,6 @@ struct bus_options {
u16 max_rec; /* Maximum packet size node can receive */
};
-
#define UNIT_DIRECTORY_VENDOR_ID 0x01
#define UNIT_DIRECTORY_MODEL_ID 0x02
#define UNIT_DIRECTORY_SPECIFIER_ID 0x04
@@ -59,8 +64,8 @@ struct bus_options {
* unit directory for each of these protocols.
*/
struct unit_directory {
- struct node_entry *ne; /* The node which this directory belongs to */
- octlet_t address; /* Address of the unit directory on the node */
+ struct node_entry *ne; /* The node which this directory belongs to */
+ octlet_t address; /* Address of the unit directory on the node */
u8 flags; /* Indicates which entries were read */
quadlet_t vendor_id;
@@ -79,11 +84,10 @@ struct unit_directory {
int length; /* Number of quadlets */
struct device device;
-
struct class_device class_dev;
struct csr1212_keyval *ud_kv;
- u32 lun; /* logical unit number immediate value */
+ u32 lun; /* logical unit number immediate value */
};
struct node_entry {
@@ -103,10 +107,8 @@ struct node_entry {
const char *vendor_oui;
u32 capabilities;
- struct hpsb_tlabel_pool *tpool;
struct device device;
-
struct class_device class_dev;
/* Means this node is not attached anymore */
@@ -153,8 +155,8 @@ static inline int hpsb_node_entry_valid(struct node_entry *ne)
/*
* This will fill in the given, pre-initialised hpsb_packet with the current
* information from the node entry (host, node ID, generation number). It will
- * return false if the node owning the GUID is not accessible (and not modify the
- * hpsb_packet) and return true otherwise.
+ * return false if the node owning the GUID is not accessible (and not modify
+ * the hpsb_packet) and return true otherwise.
*
* Note that packet sending may still fail in hpsb_send_packet if a bus reset
* happens while you are trying to set up the packet (due to obsolete generation
@@ -170,16 +172,13 @@ int hpsb_node_write(struct node_entry *ne, u64 addr,
int hpsb_node_lock(struct node_entry *ne, u64 addr,
int extcode, quadlet_t *data, quadlet_t arg);
-
/* Iterate the hosts, calling a given function with supplied data for each
* host. */
int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *));
-
int init_ieee1394_nodemgr(void);
void cleanup_ieee1394_nodemgr(void);
-
/* The template for a host device */
extern struct device nodemgr_dev_template_host;
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
index 448df277337..8fd0030475b 100644
--- a/drivers/ieee1394/ohci1394.c
+++ b/drivers/ieee1394/ohci1394.c
@@ -136,7 +136,7 @@
#define DBGMSG(fmt, args...) \
printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
#else
-#define DBGMSG(fmt, args...)
+#define DBGMSG(fmt, args...) do {} while (0)
#endif
#ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG
@@ -148,8 +148,8 @@ printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->
--global_outstanding_dmas, ## args)
static int global_outstanding_dmas = 0;
#else
-#define OHCI_DMA_ALLOC(fmt, args...)
-#define OHCI_DMA_FREE(fmt, args...)
+#define OHCI_DMA_ALLOC(fmt, args...) do {} while (0)
+#define OHCI_DMA_FREE(fmt, args...) do {} while (0)
#endif
/* print general (card independent) information */
@@ -181,36 +181,35 @@ static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
static void ohci1394_pci_remove(struct pci_dev *pdev);
#ifndef __LITTLE_ENDIAN
-static unsigned hdr_sizes[] =
-{
+const static size_t hdr_sizes[] = {
3, /* TCODE_WRITEQ */
4, /* TCODE_WRITEB */
3, /* TCODE_WRITE_RESPONSE */
- 0, /* ??? */
+ 0, /* reserved */
3, /* TCODE_READQ */
4, /* TCODE_READB */
3, /* TCODE_READQ_RESPONSE */
4, /* TCODE_READB_RESPONSE */
- 1, /* TCODE_CYCLE_START (???) */
+ 1, /* TCODE_CYCLE_START */
4, /* TCODE_LOCK_REQUEST */
2, /* TCODE_ISO_DATA */
4, /* TCODE_LOCK_RESPONSE */
+ /* rest is reserved or link-internal */
};
-/* Swap headers */
-static inline void packet_swab(quadlet_t *data, int tcode)
+static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode)
{
- size_t size = hdr_sizes[tcode];
+ size_t size;
- if (tcode > TCODE_LOCK_RESPONSE || hdr_sizes[tcode] == 0)
+ if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes)))
return;
+ size = hdr_sizes[tcode];
while (size--)
- data[size] = swab32(data[size]);
+ data[size] = le32_to_cpu(data[size]);
}
#else
-/* Don't waste cycles on same sex byte swaps */
-#define packet_swab(w,x)
+#define header_le32_to_cpu(w,x) do {} while (0)
#endif /* !LITTLE_ENDIAN */
/***********************************
@@ -701,7 +700,7 @@ static void insert_packet(struct ti_ohci *ohci,
d->prg_cpu[idx]->data[2] = packet->header[2];
d->prg_cpu[idx]->data[3] = packet->header[3];
}
- packet_swab(d->prg_cpu[idx]->data, packet->tcode);
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
}
if (packet->data_size) { /* block transmit */
@@ -777,7 +776,7 @@ static void insert_packet(struct ti_ohci *ohci,
d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
(packet->header[0] & 0xFFFF);
d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
- packet_swab(d->prg_cpu[idx]->data, packet->tcode);
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
d->prg_cpu[idx]->begin.control =
cpu_to_le32(DMA_CTL_OUTPUT_MORE |
@@ -2598,8 +2597,9 @@ static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0,
* Determine the length of a packet in the buffer
* Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca>
*/
-static __inline__ int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr,
- int offset, unsigned char tcode, int noswap)
+static inline int packet_length(struct dma_rcv_ctx *d, int idx,
+ quadlet_t *buf_ptr, int offset,
+ unsigned char tcode, int noswap)
{
int length = -1;
@@ -2730,7 +2730,7 @@ static void dma_rcv_tasklet (unsigned long data)
* bus reset. We always ignore it. */
if (tcode != OHCI1394_TCODE_PHY) {
if (!ohci->no_swap_incoming)
- packet_swab(d->spb, tcode);
+ header_le32_to_cpu(d->spb, tcode);
DBGMSG("Packet received from node"
" %d ack=0x%02X spd=%d tcode=0x%X"
" length=%d ctx=%d tlabel=%d",
@@ -2738,7 +2738,7 @@ static void dma_rcv_tasklet (unsigned long data)
(cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f,
(cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3,
tcode, length, d->ctx,
- (cond_le32_to_cpu(d->spb[0], ohci->no_swap_incoming)>>10)&0x3f);
+ (d->spb[0]>>10)&0x3f);
ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f)
== 0x11) ? 1 : 0;
@@ -3529,9 +3529,10 @@ static void ohci1394_pci_remove(struct pci_dev *pdev)
put_device(dev);
}
-
+#ifdef CONFIG_PM
static int ohci1394_pci_resume (struct pci_dev *pdev)
{
+/* PowerMac resume code comes first */
#ifdef CONFIG_PPC_PMAC
if (machine_is(powermac)) {
struct device_node *of_node;
@@ -3543,17 +3544,23 @@ static int ohci1394_pci_resume (struct pci_dev *pdev)
}
#endif /* CONFIG_PPC_PMAC */
+ pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_enable_device(pdev);
-
- return 0;
+ return pci_enable_device(pdev);
}
-
static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state)
{
- pci_save_state(pdev);
+ int err;
+
+ err = pci_save_state(pdev);
+ if (err)
+ goto out;
+ err = pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ if (err)
+ goto out;
+/* PowerMac suspend code comes last */
#ifdef CONFIG_PPC_PMAC
if (machine_is(powermac)) {
struct device_node *of_node;
@@ -3563,11 +3570,11 @@ static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state)
if (of_node)
pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0);
}
-#endif
-
- return 0;
+#endif /* CONFIG_PPC_PMAC */
+out:
+ return err;
}
-
+#endif /* CONFIG_PM */
#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
@@ -3590,8 +3597,10 @@ static struct pci_driver ohci1394_pci_driver = {
.id_table = ohci1394_pci_tbl,
.probe = ohci1394_pci_probe,
.remove = ohci1394_pci_remove,
+#ifdef CONFIG_PM
.resume = ohci1394_pci_resume,
.suspend = ohci1394_pci_suspend,
+#endif
};
/***********************************
@@ -3718,5 +3727,7 @@ static int __init ohci1394_init(void)
return pci_register_driver(&ohci1394_pci_driver);
}
-module_init(ohci1394_init);
+/* Register before most other device drivers.
+ * Useful for remote debugging via physical DMA, e.g. using firescope. */
+fs_initcall(ohci1394_init);
module_exit(ohci1394_cleanup);
diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c
index e6f41238f5e..b4f146f2c95 100644
--- a/drivers/ieee1394/pcilynx.c
+++ b/drivers/ieee1394/pcilynx.c
@@ -137,7 +137,6 @@ static struct i2c_algo_bit_data bit_data = {
.getsda = bit_getsda,
.getscl = bit_getscl,
.udelay = 5,
- .mdelay = 5,
.timeout = 100,
};
diff --git a/drivers/ieee1394/raw1394-private.h b/drivers/ieee1394/raw1394-private.h
index c93587be9ca..c7731d1bcd8 100644
--- a/drivers/ieee1394/raw1394-private.h
+++ b/drivers/ieee1394/raw1394-private.h
@@ -29,9 +29,8 @@ struct file_info {
struct list_head req_pending;
struct list_head req_complete;
- struct semaphore complete_sem;
spinlock_t reqlists_lock;
- wait_queue_head_t poll_wait_complete;
+ wait_queue_head_t wait_complete;
struct list_head addr_list;
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
index 571ea68c0cf..5ec4f5eb6b1 100644
--- a/drivers/ieee1394/raw1394.c
+++ b/drivers/ieee1394/raw1394.c
@@ -44,14 +44,15 @@
#include <linux/compat.h>
#include "csr1212.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
-#include "ieee1394_types.h"
#include "ieee1394_core.h"
-#include "nodemgr.h"
-#include "hosts.h"
-#include "highlevel.h"
-#include "iso.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "iso.h"
+#include "nodemgr.h"
#include "raw1394.h"
#include "raw1394-private.h"
@@ -66,7 +67,7 @@
#define DBGMSG(fmt, args...) \
printk(KERN_INFO "raw1394:" fmt "\n" , ## args)
#else
-#define DBGMSG(fmt, args...)
+#define DBGMSG(fmt, args...) do {} while (0)
#endif
static LIST_HEAD(host_info_list);
@@ -132,10 +133,9 @@ static void free_pending_request(struct pending_request *req)
static void __queue_complete_req(struct pending_request *req)
{
struct file_info *fi = req->file_info;
- list_move_tail(&req->list, &fi->req_complete);
- up(&fi->complete_sem);
- wake_up_interruptible(&fi->poll_wait_complete);
+ list_move_tail(&req->list, &fi->req_complete);
+ wake_up(&fi->wait_complete);
}
static void queue_complete_req(struct pending_request *req)
@@ -463,13 +463,36 @@ raw1394_compat_read(const char __user *buf, struct raw1394_request *r)
#endif
+/* get next completed request (caller must hold fi->reqlists_lock) */
+static inline struct pending_request *__next_complete_req(struct file_info *fi)
+{
+ struct list_head *lh;
+ struct pending_request *req = NULL;
+
+ if (!list_empty(&fi->req_complete)) {
+ lh = fi->req_complete.next;
+ list_del(lh);
+ req = list_entry(lh, struct pending_request, list);
+ }
+ return req;
+}
+
+/* atomically get next completed request */
+static struct pending_request *next_complete_req(struct file_info *fi)
+{
+ unsigned long flags;
+ struct pending_request *req;
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ req = __next_complete_req(fi);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+ return req;
+}
static ssize_t raw1394_read(struct file *file, char __user * buffer,
size_t count, loff_t * offset_is_ignored)
{
- unsigned long flags;
struct file_info *fi = (struct file_info *)file->private_data;
- struct list_head *lh;
struct pending_request *req;
ssize_t ret;
@@ -487,22 +510,21 @@ static ssize_t raw1394_read(struct file *file, char __user * buffer,
}
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&fi->complete_sem)) {
+ if (!(req = next_complete_req(fi)))
return -EAGAIN;
- }
} else {
- if (down_interruptible(&fi->complete_sem)) {
+ /*
+ * NB: We call the macro wait_event_interruptible() with a
+ * condition argument with side effect. This is only possible
+ * because the side effect does not occur until the condition
+ * became true, and wait_event_interruptible() won't evaluate
+ * the condition again after that.
+ */
+ if (wait_event_interruptible(fi->wait_complete,
+ (req = next_complete_req(fi))))
return -ERESTARTSYS;
- }
}
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- lh = fi->req_complete.next;
- list_del(lh);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-
- req = list_entry(lh, struct pending_request, list);
-
if (req->req.length) {
if (copy_to_user(int2ptr(req->req.recvb), req->data,
req->req.length)) {
@@ -1752,6 +1774,7 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
addr->notification_options |= addr->client_transactions;
addr->recvb = req->req.recvb;
addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF);
+
spin_lock_irqsave(&host_info_lock, flags);
hi = find_host_info(fi->host);
same_host = 0;
@@ -1777,9 +1800,9 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
}
if (same_host) {
/* addressrange occupied by same host */
+ spin_unlock_irqrestore(&host_info_lock, flags);
vfree(addr->addr_space_buffer);
kfree(addr);
- spin_unlock_irqrestore(&host_info_lock, flags);
return (-EALREADY);
}
/* another host with valid address-entry containing same addressrange */
@@ -1807,6 +1830,8 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
}
}
}
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
if (another_host) {
DBGMSG("another hosts entry is valid -> SUCCESS");
if (copy_to_user(int2ptr(req->req.recvb),
@@ -1815,11 +1840,11 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
" address-range-entry is invalid -> EFAULT !!!\n");
vfree(addr->addr_space_buffer);
kfree(addr);
- spin_unlock_irqrestore(&host_info_lock, flags);
return (-EFAULT);
}
free_pending_request(req); /* immediate success or fail */
/* INSERT ENTRY */
+ spin_lock_irqsave(&host_info_lock, flags);
list_add_tail(&addr->addr_list, &fi->addr_list);
spin_unlock_irqrestore(&host_info_lock, flags);
return sizeof(struct raw1394_request);
@@ -1830,15 +1855,15 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
req->req.address + req->req.length);
if (retval) {
/* INSERT ENTRY */
+ spin_lock_irqsave(&host_info_lock, flags);
list_add_tail(&addr->addr_list, &fi->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
} else {
DBGMSG("arm_register failed errno: %d \n", retval);
vfree(addr->addr_space_buffer);
kfree(addr);
- spin_unlock_irqrestore(&host_info_lock, flags);
return (-EALREADY);
}
- spin_unlock_irqrestore(&host_info_lock, flags);
free_pending_request(req); /* immediate success or fail */
return sizeof(struct raw1394_request);
}
@@ -1904,10 +1929,10 @@ static int arm_unregister(struct file_info *fi, struct pending_request *req)
if (another_host) {
DBGMSG("delete entry from list -> success");
list_del(&addr->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
vfree(addr->addr_space_buffer);
kfree(addr);
free_pending_request(req); /* immediate success or fail */
- spin_unlock_irqrestore(&host_info_lock, flags);
return sizeof(struct raw1394_request);
}
retval =
@@ -1949,23 +1974,19 @@ static int arm_get_buf(struct file_info *fi, struct pending_request *req)
(arm_addr->end > req->req.address)) {
if (req->req.address + req->req.length <= arm_addr->end) {
offset = req->req.address - arm_addr->start;
+ spin_unlock_irqrestore(&host_info_lock, flags);
DBGMSG
("arm_get_buf copy_to_user( %08X, %p, %u )",
(u32) req->req.recvb,
arm_addr->addr_space_buffer + offset,
(u32) req->req.length);
-
if (copy_to_user
(int2ptr(req->req.recvb),
arm_addr->addr_space_buffer + offset,
- req->req.length)) {
- spin_unlock_irqrestore(&host_info_lock,
- flags);
+ req->req.length))
return (-EFAULT);
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
/* We have to free the request, because we
* queue no response, and therefore nobody
* will free it. */
@@ -2005,24 +2026,23 @@ static int arm_set_buf(struct file_info *fi, struct pending_request *req)
(arm_addr->end > req->req.address)) {
if (req->req.address + req->req.length <= arm_addr->end) {
offset = req->req.address - arm_addr->start;
+ spin_unlock_irqrestore(&host_info_lock, flags);
DBGMSG
("arm_set_buf copy_from_user( %p, %08X, %u )",
arm_addr->addr_space_buffer + offset,
(u32) req->req.sendb,
(u32) req->req.length);
-
if (copy_from_user
(arm_addr->addr_space_buffer + offset,
int2ptr(req->req.sendb),
- req->req.length)) {
- spin_unlock_irqrestore(&host_info_lock,
- flags);
+ req->req.length))
return (-EFAULT);
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
- free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
+ /* We have to free the request, because we
+ * queue no response, and therefore nobody
+ * will free it. */
+ free_pending_request(req);
return sizeof(struct raw1394_request);
} else {
DBGMSG("arm_set_buf request exceeded mapping");
@@ -2744,7 +2764,7 @@ static unsigned int raw1394_poll(struct file *file, poll_table * pt)
unsigned int mask = POLLOUT | POLLWRNORM;
unsigned long flags;
- poll_wait(file, &fi->poll_wait_complete, pt);
+ poll_wait(file, &fi->wait_complete, pt);
spin_lock_irqsave(&fi->reqlists_lock, flags);
if (!list_empty(&fi->req_complete)) {
@@ -2769,9 +2789,8 @@ static int raw1394_open(struct inode *inode, struct file *file)
fi->state = opened;
INIT_LIST_HEAD(&fi->req_pending);
INIT_LIST_HEAD(&fi->req_complete);
- sema_init(&fi->complete_sem, 0);
spin_lock_init(&fi->reqlists_lock);
- init_waitqueue_head(&fi->poll_wait_complete);
+ init_waitqueue_head(&fi->wait_complete);
INIT_LIST_HEAD(&fi->addr_list);
file->private_data = fi;
@@ -2784,7 +2803,7 @@ static int raw1394_release(struct inode *inode, struct file *file)
struct file_info *fi = file->private_data;
struct list_head *lh;
struct pending_request *req;
- int done = 0, i, fail = 0;
+ int i, fail;
int retval = 0;
struct list_head *entry;
struct arm_addr *addr = NULL;
@@ -2864,25 +2883,28 @@ static int raw1394_release(struct inode *inode, struct file *file)
"error(s) occurred \n");
}
- while (!done) {
+ for (;;) {
+ /* This locked section guarantees that neither
+ * complete nor pending requests exist once i!=0 */
spin_lock_irqsave(&fi->reqlists_lock, flags);
-
- while (!list_empty(&fi->req_complete)) {
- lh = fi->req_complete.next;
- list_del(lh);
-
- req = list_entry(lh, struct pending_request, list);
-
+ while ((req = __next_complete_req(fi)))
free_pending_request(req);
- }
-
- if (list_empty(&fi->req_pending))
- done = 1;
+ i = list_empty(&fi->req_pending);
spin_unlock_irqrestore(&fi->reqlists_lock, flags);
- if (!done)
- down_interruptible(&fi->complete_sem);
+ if (i)
+ break;
+ /*
+ * Sleep until more requests can be freed.
+ *
+ * NB: We call the macro wait_event() with a condition argument
+ * with side effect. This is only possible because the side
+ * effect does not occur until the condition became true, and
+ * wait_event() won't evaluate the condition again after that.
+ */
+ wait_event(fi->wait_complete, (req = next_complete_req(fi)));
+ free_pending_request(req);
}
/* Remove any sub-trees left by user space programs */
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
index b08755e2e68..6986ac18828 100644
--- a/drivers/ieee1394/sbp2.c
+++ b/drivers/ieee1394/sbp2.c
@@ -38,31 +38,36 @@
* but the code needs additional debugging.
*/
+#include <linux/blkdev.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
-#include <linux/string.h>
-#include <linux/stringify.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/blkdev.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/wait.h>
-#include <asm/current.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
#include <asm/byteorder.h>
-#include <asm/atomic.h>
-#include <asm/system.h>
+#include <asm/errno.h>
+#include <asm/param.h>
#include <asm/scatterlist.h>
+#include <asm/system.h>
+#include <asm/types.h>
+
+#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
+#include <asm/io.h> /* for bus_to_virt */
+#endif
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -71,13 +76,14 @@
#include <scsi/scsi_host.h>
#include "csr1212.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
-#include "ieee1394_types.h"
#include "ieee1394_core.h"
-#include "nodemgr.h"
-#include "hosts.h"
-#include "highlevel.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "nodemgr.h"
#include "sbp2.h"
/*
@@ -173,11 +179,6 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)");
-/* legacy parameter */
-static int force_inquiry_hack;
-module_param(force_inquiry_hack, int, 0644);
-MODULE_PARM_DESC(force_inquiry_hack, "Deprecated, use 'workarounds'");
-
/*
* Export information about protocols/devices supported by this driver.
*/
@@ -208,9 +209,9 @@ static u32 global_outstanding_command_orbs = 0;
#define outstanding_orb_incr global_outstanding_command_orbs++
#define outstanding_orb_decr global_outstanding_command_orbs--
#else
-#define SBP2_ORB_DEBUG(fmt, args...)
-#define outstanding_orb_incr
-#define outstanding_orb_decr
+#define SBP2_ORB_DEBUG(fmt, args...) do {} while (0)
+#define outstanding_orb_incr do {} while (0)
+#define outstanding_orb_decr do {} while (0)
#endif
#ifdef CONFIG_IEEE1394_SBP2_DEBUG_DMA
@@ -222,8 +223,8 @@ static u32 global_outstanding_command_orbs = 0;
--global_outstanding_dmas, ## args)
static u32 global_outstanding_dmas = 0;
#else
-#define SBP2_DMA_ALLOC(fmt, args...)
-#define SBP2_DMA_FREE(fmt, args...)
+#define SBP2_DMA_ALLOC(fmt, args...) do {} while (0)
+#define SBP2_DMA_FREE(fmt, args...) do {} while (0)
#endif
#if CONFIG_IEEE1394_SBP2_DEBUG >= 2
@@ -237,7 +238,7 @@ static u32 global_outstanding_dmas = 0;
#define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args)
#define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args)
#else
-#define SBP2_DEBUG(fmt, args...)
+#define SBP2_DEBUG(fmt, args...) do {} while (0)
#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args)
#define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args)
#define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args)
@@ -356,7 +357,7 @@ static const struct {
/*
* Converts a buffer from be32 to cpu byte ordering. Length is in bytes.
*/
-static __inline__ void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
+static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
{
u32 *temp = buffer;
@@ -369,7 +370,7 @@ static __inline__ void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
/*
* Converts a buffer from cpu to be32 byte ordering. Length is in bytes.
*/
-static __inline__ void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
+static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
{
u32 *temp = buffer;
@@ -380,8 +381,8 @@ static __inline__ void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
}
#else /* BIG_ENDIAN */
/* Why waste the cpu cycles? */
-#define sbp2util_be32_to_cpu_buffer(x,y)
-#define sbp2util_cpu_to_be32_buffer(x,y)
+#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0)
+#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0)
#endif
#ifdef CONFIG_IEEE1394_SBP2_PACKET_DUMP
@@ -417,24 +418,26 @@ static void sbp2util_packet_dump(void *buffer, int length, char *dump_name,
return;
}
#else
-#define sbp2util_packet_dump(w,x,y,z)
+#define sbp2util_packet_dump(w,x,y,z) do {} while (0)
#endif
+static DECLARE_WAIT_QUEUE_HEAD(access_wq);
+
/*
- * Goofy routine that basically does a down_timeout function.
+ * Waits for completion of an SBP-2 access request.
+ * Returns nonzero if timed out or prematurely interrupted.
*/
-static int sbp2util_down_timeout(atomic_t *done, int timeout)
+static int sbp2util_access_timeout(struct scsi_id_instance_data *scsi_id,
+ int timeout)
{
- int i;
+ long leftover = wait_event_interruptible_timeout(
+ access_wq, scsi_id->access_complete, timeout);
- for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) {
- if (msleep_interruptible(100)) /* 100ms */
- return 1;
- }
- return (i > 0) ? 0 : 1;
+ scsi_id->access_complete = 0;
+ return leftover <= 0;
}
-/* Free's an allocated packet */
+/* Frees an allocated packet */
static void sbp2_free_packet(struct hpsb_packet *packet)
{
hpsb_free_tlabel(packet);
@@ -468,6 +471,44 @@ static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr,
return 0;
}
+static void sbp2util_notify_fetch_agent(struct scsi_id_instance_data *scsi_id,
+ u64 offset, quadlet_t *data, size_t len)
+{
+ /*
+ * There is a small window after a bus reset within which the node
+ * entry's generation is current but the reconnect wasn't completed.
+ */
+ if (unlikely(atomic_read(&scsi_id->state) == SBP2LU_STATE_IN_RESET))
+ return;
+
+ if (hpsb_node_write(scsi_id->ne,
+ scsi_id->sbp2_command_block_agent_addr + offset,
+ data, len))
+ SBP2_ERR("sbp2util_notify_fetch_agent failed.");
+ /*
+ * Now accept new SCSI commands, unless a bus reset happended during
+ * hpsb_node_write.
+ */
+ if (likely(atomic_read(&scsi_id->state) != SBP2LU_STATE_IN_RESET))
+ scsi_unblock_requests(scsi_id->scsi_host);
+}
+
+static void sbp2util_write_orb_pointer(void *p)
+{
+ quadlet_t data[2];
+
+ data[0] = ORB_SET_NODE_ID(
+ ((struct scsi_id_instance_data *)p)->hi->host->node_id);
+ data[1] = ((struct scsi_id_instance_data *)p)->last_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+ sbp2util_notify_fetch_agent(p, SBP2_ORB_POINTER_OFFSET, data, 8);
+}
+
+static void sbp2util_write_doorbell(void *p)
+{
+ sbp2util_notify_fetch_agent(p, SBP2_DOORBELL_OFFSET, NULL, 4);
+}
+
/*
* This function is called to create a pool of command orbs used for
* command processing. It is called when a new sbp2 device is detected.
@@ -492,7 +533,7 @@ static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_i
command->command_orb_dma =
pci_map_single(hi->host->pdev, &command->command_orb,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
SBP2_DMA_ALLOC("single command orb DMA");
command->sge_dma =
pci_map_single(hi->host->pdev,
@@ -525,7 +566,7 @@ static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_
/* Release our generic DMA's */
pci_unmap_single(host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
SBP2_DMA_FREE("single command orb DMA");
pci_unmap_single(host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
@@ -715,6 +756,7 @@ static int sbp2_remove(struct device *dev)
sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT);
/* scsi_remove_device() will trigger shutdown functions of SCSI
* highlevel drivers which would deadlock if blocked. */
+ atomic_set(&scsi_id->state, SBP2LU_STATE_IN_SHUTDOWN);
scsi_unblock_requests(scsi_id->scsi_host);
}
sdev = scsi_id->sdev;
@@ -766,10 +808,12 @@ static int sbp2_update(struct unit_directory *ud)
*/
sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
- /* Make sure we unblock requests (since this is likely after a bus
- * reset). */
- scsi_unblock_requests(scsi_id->scsi_host);
-
+ /* Accept new commands unless there was another bus reset in the
+ * meantime. */
+ if (hpsb_node_entry_valid(scsi_id->ne)) {
+ atomic_set(&scsi_id->state, SBP2LU_STATE_RUNNING);
+ scsi_unblock_requests(scsi_id->scsi_host);
+ }
return 0;
}
@@ -794,11 +838,12 @@ static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud
scsi_id->speed_code = IEEE1394_SPEED_100;
scsi_id->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100];
scsi_id->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE;
- atomic_set(&scsi_id->sbp2_login_complete, 0);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed);
INIT_LIST_HEAD(&scsi_id->scsi_list);
spin_lock_init(&scsi_id->sbp2_command_orb_lock);
+ atomic_set(&scsi_id->state, SBP2LU_STATE_RUNNING);
+ INIT_WORK(&scsi_id->protocol_work, NULL, NULL);
ud->device.driver_data = scsi_id;
@@ -881,11 +926,14 @@ static void sbp2_host_reset(struct hpsb_host *host)
struct scsi_id_instance_data *scsi_id;
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
-
- if (hi) {
- list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list)
+ if (!hi)
+ return;
+ list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list)
+ if (likely(atomic_read(&scsi_id->state) !=
+ SBP2LU_STATE_IN_SHUTDOWN)) {
+ atomic_set(&scsi_id->state, SBP2LU_STATE_IN_RESET);
scsi_block_requests(scsi_id->scsi_host);
- }
+ }
}
/*
@@ -970,8 +1018,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
* connected to the sbp2 device being removed. That host would
* have a certain amount of time to relogin before the sbp2 device
* allows someone else to login instead. One second makes sense. */
- msleep_interruptible(1000);
- if (signal_pending(current)) {
+ if (msleep_interruptible(1000)) {
sbp2_remove_device(scsi_id);
return -EINTR;
}
@@ -1036,7 +1083,7 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
scsi_remove_host(scsi_id->scsi_host);
scsi_host_put(scsi_id->scsi_host);
}
-
+ flush_scheduled_work();
sbp2util_remove_command_orb_pool(scsi_id);
list_del(&scsi_id->scsi_list);
@@ -1182,17 +1229,14 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id)
"sbp2 query logins orb", scsi_id->query_logins_orb_dma);
memset(scsi_id->query_logins_response, 0, sizeof(struct sbp2_query_logins_response));
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = scsi_id->query_logins_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8);
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 2*HZ)) {
+ if (sbp2util_access_timeout(scsi_id, 2*HZ)) {
SBP2_INFO("Error querying logins to SBP-2 device - timed out");
return -EIO;
}
@@ -1202,11 +1246,8 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id)
return -EIO;
}
- if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- SBP2_INFO("Error querying logins to SBP-2 device - timed out");
+ if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) {
+ SBP2_INFO("Error querying logins to SBP-2 device - failed");
return -EIO;
}
@@ -1278,21 +1319,18 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
"sbp2 login orb", scsi_id->login_orb_dma);
memset(scsi_id->login_response, 0, sizeof(struct sbp2_login_response));
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = scsi_id->login_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8);
/*
* Wait for login status (up to 20 seconds)...
*/
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 20*HZ)) {
- SBP2_ERR("Error logging into SBP-2 device - login timed-out");
+ if (sbp2util_access_timeout(scsi_id, 20*HZ)) {
+ SBP2_ERR("Error logging into SBP-2 device - timed out");
return -EIO;
}
@@ -1300,18 +1338,12 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
* Sanity. Make sure status returned matches login orb.
*/
if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma) {
- SBP2_ERR("Error logging into SBP-2 device - login timed-out");
+ SBP2_ERR("Error logging into SBP-2 device - timed out");
return -EIO;
}
- /*
- * Check status
- */
- if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- SBP2_ERR("Error logging into SBP-2 device - login failed");
+ if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) {
+ SBP2_ERR("Error logging into SBP-2 device - failed");
return -EIO;
}
@@ -1335,9 +1367,7 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
scsi_id->sbp2_command_block_agent_addr &= 0x0000ffffffffffffULL;
SBP2_INFO("Logged into SBP-2 device");
-
return 0;
-
}
/*
@@ -1387,21 +1417,17 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id)
data[1] = scsi_id->logout_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
error = hpsb_node_write(scsi_id->ne,
scsi_id->sbp2_management_agent_addr, data, 8);
if (error)
return error;
/* Wait for device to logout...1 second. */
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ))
+ if (sbp2util_access_timeout(scsi_id, HZ))
return -EIO;
SBP2_INFO("Logged out of SBP-2 device");
-
return 0;
-
}
/*
@@ -1445,20 +1471,10 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
sbp2util_packet_dump(scsi_id->reconnect_orb, sizeof(struct sbp2_reconnect_orb),
"sbp2 reconnect orb", scsi_id->reconnect_orb_dma);
- /*
- * Initialize status fifo
- */
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
-
- /*
- * Ok, let's write to the target's management agent register
- */
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = scsi_id->reconnect_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
error = hpsb_node_write(scsi_id->ne,
scsi_id->sbp2_management_agent_addr, data, 8);
if (error)
@@ -1467,8 +1483,8 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
/*
* Wait for reconnect status (up to 1 second)...
*/
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ)) {
- SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out");
+ if (sbp2util_access_timeout(scsi_id, HZ)) {
+ SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
return -EIO;
}
@@ -1476,25 +1492,17 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
* Sanity. Make sure status returned matches reconnect orb.
*/
if (scsi_id->status_block.ORB_offset_lo != scsi_id->reconnect_orb_dma) {
- SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out");
+ SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
return -EIO;
}
- /*
- * Check status
- */
- if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- SBP2_ERR("Error reconnecting to SBP-2 device - reconnect failed");
+ if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) {
+ SBP2_ERR("Error reconnecting to SBP-2 device - failed");
return -EIO;
}
HPSB_DEBUG("Reconnected to SBP-2 device");
-
return 0;
-
}
/*
@@ -1592,11 +1600,6 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id,
}
workarounds = sbp2_default_workarounds;
- if (force_inquiry_hack) {
- SBP2_WARN("force_inquiry_hack is deprecated. "
- "Use parameter 'workarounds' instead.");
- workarounds |= SBP2_WORKAROUND_INQUIRY_36;
- }
if (!(workarounds & SBP2_WORKAROUND_OVERRIDE))
for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) {
@@ -1705,9 +1708,14 @@ static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait)
quadlet_t data;
u64 addr;
int retval;
+ unsigned long flags;
SBP2_DEBUG_ENTER();
+ cancel_delayed_work(&scsi_id->protocol_work);
+ if (wait)
+ flush_scheduled_work();
+
data = ntohl(SBP2_AGENT_RESET_DATA);
addr = scsi_id->sbp2_command_block_agent_addr + SBP2_AGENT_RESET_OFFSET;
@@ -1724,7 +1732,9 @@ static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait)
/*
* Need to make sure orb pointer is written on next command
*/
+ spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
scsi_id->last_orb = NULL;
+ spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
return 0;
}
@@ -1961,13 +1971,17 @@ static void sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id,
/*
* This function is called in order to begin a regular SBP-2 command.
*/
-static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
+static void sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
struct sbp2_command_info *command)
{
struct sbp2scsi_host_info *hi = scsi_id->hi;
struct sbp2_command_orb *command_orb = &command->command_orb;
- struct node_entry *ne = scsi_id->ne;
- u64 addr;
+ struct sbp2_command_orb *last_orb;
+ dma_addr_t last_orb_dma;
+ u64 addr = scsi_id->sbp2_command_block_agent_addr;
+ quadlet_t data[2];
+ size_t length;
+ unsigned long flags;
outstanding_orb_incr;
SBP2_ORB_DEBUG("sending command orb %p, total orbs = %x",
@@ -1975,73 +1989,70 @@ static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
pci_dma_sync_single_for_device(hi->host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_device(hi->host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
PCI_DMA_BIDIRECTIONAL);
/*
* Check to see if there are any previous orbs to use
*/
- if (scsi_id->last_orb == NULL) {
- quadlet_t data[2];
-
+ spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
+ last_orb = scsi_id->last_orb;
+ last_orb_dma = scsi_id->last_orb_dma;
+ if (!last_orb) {
/*
- * Ok, let's write to the target's management agent register
+ * last_orb == NULL means: We know that the target's fetch agent
+ * is not active right now.
*/
- addr = scsi_id->sbp2_command_block_agent_addr + SBP2_ORB_POINTER_OFFSET;
+ addr += SBP2_ORB_POINTER_OFFSET;
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = command->command_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
-
- SBP2_ORB_DEBUG("write command agent, command orb %p", command_orb);
-
- if (sbp2util_node_write_no_wait(ne, addr, data, 8) < 0) {
- SBP2_ERR("sbp2util_node_write_no_wait failed.\n");
- return -EIO;
- }
-
- SBP2_ORB_DEBUG("write command agent complete");
-
- scsi_id->last_orb = command_orb;
- scsi_id->last_orb_dma = command->command_orb_dma;
-
+ length = 8;
} else {
- quadlet_t data;
-
/*
- * We have an orb already sent (maybe or maybe not
- * processed) that we can append this orb to. So do so,
- * and ring the doorbell. Have to be very careful
- * modifying these next orb pointers, as they are accessed
- * both by the sbp2 device and us.
+ * last_orb != NULL means: We know that the target's fetch agent
+ * is (very probably) not dead or in reset state right now.
+ * We have an ORB already sent that we can append a new one to.
+ * The target's fetch agent may or may not have read this
+ * previous ORB yet.
*/
- scsi_id->last_orb->next_ORB_lo =
- cpu_to_be32(command->command_orb_dma);
+ pci_dma_sync_single_for_cpu(hi->host->pdev, last_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ PCI_DMA_TODEVICE);
+ last_orb->next_ORB_lo = cpu_to_be32(command->command_orb_dma);
+ wmb();
/* Tells hardware that this pointer is valid */
- scsi_id->last_orb->next_ORB_hi = 0x0;
- pci_dma_sync_single_for_device(hi->host->pdev,
- scsi_id->last_orb_dma,
+ last_orb->next_ORB_hi = 0;
+ pci_dma_sync_single_for_device(hi->host->pdev, last_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
+ addr += SBP2_DOORBELL_OFFSET;
+ data[0] = 0;
+ length = 4;
+ }
+ scsi_id->last_orb = command_orb;
+ scsi_id->last_orb_dma = command->command_orb_dma;
+ spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
+ SBP2_ORB_DEBUG("write to %s register, command orb %p",
+ last_orb ? "DOORBELL" : "ORB_POINTER", command_orb);
+ if (sbp2util_node_write_no_wait(scsi_id->ne, addr, data, length)) {
/*
- * Ring the doorbell
+ * sbp2util_node_write_no_wait failed. We certainly ran out
+ * of transaction labels, perhaps just because there were no
+ * context switches which gave khpsbpkt a chance to collect
+ * free tlabels. Try again in non-atomic context. If necessary,
+ * the workqueue job will sleep to guaranteedly get a tlabel.
+ * We do not accept new commands until the job is over.
*/
- data = cpu_to_be32(command->command_orb_dma);
- addr = scsi_id->sbp2_command_block_agent_addr + SBP2_DOORBELL_OFFSET;
-
- SBP2_ORB_DEBUG("ring doorbell, command orb %p", command_orb);
-
- if (sbp2util_node_write_no_wait(ne, addr, &data, 4) < 0) {
- SBP2_ERR("sbp2util_node_write_no_wait failed");
- return -EIO;
- }
-
- scsi_id->last_orb = command_orb;
- scsi_id->last_orb_dma = command->command_orb_dma;
-
+ scsi_block_requests(scsi_id->scsi_host);
+ PREPARE_WORK(&scsi_id->protocol_work,
+ last_orb ? sbp2util_write_doorbell:
+ sbp2util_write_orb_pointer,
+ scsi_id);
+ schedule_work(&scsi_id->protocol_work);
}
- return 0;
}
/*
@@ -2078,11 +2089,6 @@ static int sbp2_send_command(struct scsi_id_instance_data *scsi_id,
"sbp2 command orb", command->command_orb_dma);
/*
- * Initialize status fifo
- */
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
-
- /*
* Link up the orb, and ring the doorbell if needed
*/
sbp2_link_orb_command(scsi_id, command);
@@ -2123,12 +2129,14 @@ static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense
/*
* This function deals with status writes from the SBP-2 device
*/
-static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, size_t length, u16 fl)
+static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid,
+ int destid, quadlet_t *data, u64 addr,
+ size_t length, u16 fl)
{
struct sbp2scsi_host_info *hi;
struct scsi_id_instance_data *scsi_id = NULL, *scsi_id_tmp;
struct scsi_cmnd *SCpnt = NULL;
+ struct sbp2_status_block *sb;
u32 scsi_status = SBP2_SCSI_STATUS_GOOD;
struct sbp2_command_info *command;
unsigned long flags;
@@ -2137,18 +2145,19 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
sbp2util_packet_dump(data, length, "sbp2 status write by device", (u32)addr);
- if (!host) {
+ if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) {
+ SBP2_ERR("Wrong size of status block");
+ return RCODE_ADDRESS_ERROR;
+ }
+ if (unlikely(!host)) {
SBP2_ERR("host is NULL - this is bad!");
return RCODE_ADDRESS_ERROR;
}
-
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
-
- if (!hi) {
+ if (unlikely(!hi)) {
SBP2_ERR("host info is NULL - this is bad!");
return RCODE_ADDRESS_ERROR;
}
-
/*
* Find our scsi_id structure by looking at the status fifo address
* written to by the sbp2 device.
@@ -2160,32 +2169,35 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
break;
}
}
-
- if (!scsi_id) {
+ if (unlikely(!scsi_id)) {
SBP2_ERR("scsi_id is NULL - device is gone?");
return RCODE_ADDRESS_ERROR;
}
/*
- * Put response into scsi_id status fifo...
+ * Put response into scsi_id status fifo buffer. The first two bytes
+ * come in big endian bit order. Often the target writes only a
+ * truncated status block, minimally the first two quadlets. The rest
+ * is implied to be zeros.
*/
- memcpy(&scsi_id->status_block, data, length);
+ sb = &scsi_id->status_block;
+ memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent));
+ memcpy(sb, data, length);
+ sbp2util_be32_to_cpu_buffer(sb, 8);
/*
- * Byte swap first two quadlets (8 bytes) of status for processing
+ * Ignore unsolicited status. Handle command ORB status.
*/
- sbp2util_be32_to_cpu_buffer(&scsi_id->status_block, 8);
-
- /*
- * Handle command ORB status here if necessary. First, need to match status with command.
- */
- command = sbp2util_find_command_for_orb(scsi_id, scsi_id->status_block.ORB_offset_lo);
+ if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2))
+ command = NULL;
+ else
+ command = sbp2util_find_command_for_orb(scsi_id,
+ sb->ORB_offset_lo);
if (command) {
-
SBP2_DEBUG("Found status for command ORB");
pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
PCI_DMA_BIDIRECTIONAL);
@@ -2194,7 +2206,12 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
outstanding_orb_decr;
/*
- * Matched status with command, now grab scsi command pointers and check status
+ * Matched status with command, now grab scsi command pointers
+ * and check status.
+ */
+ /*
+ * FIXME: If the src field in the status is 1, the ORB DMA must
+ * not be reused until status for a subsequent ORB is received.
*/
SCpnt = command->Current_SCpnt;
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
@@ -2202,61 +2219,64 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
if (SCpnt) {
-
+ u32 h = sb->ORB_offset_hi_misc;
+ u32 r = STATUS_GET_RESP(h);
+
+ if (r != RESP_STATUS_REQUEST_COMPLETE) {
+ SBP2_WARN("resp 0x%x, sbp_status 0x%x",
+ r, STATUS_GET_SBP_STATUS(h));
+ scsi_status =
+ r == RESP_STATUS_TRANSPORT_FAILURE ?
+ SBP2_SCSI_STATUS_BUSY :
+ SBP2_SCSI_STATUS_COMMAND_TERMINATED;
+ }
/*
- * See if the target stored any scsi status information
+ * See if the target stored any scsi status information.
*/
- if (STATUS_GET_LENGTH(scsi_id->status_block.ORB_offset_hi_misc) > 1) {
- /*
- * Translate SBP-2 status to SCSI sense data
- */
+ if (STATUS_GET_LEN(h) > 1) {
SBP2_DEBUG("CHECK CONDITION");
- scsi_status = sbp2_status_to_sense_data((unchar *)&scsi_id->status_block, SCpnt->sense_buffer);
+ scsi_status = sbp2_status_to_sense_data(
+ (unchar *)sb, SCpnt->sense_buffer);
}
-
/*
- * Check to see if the dead bit is set. If so, we'll have to initiate
- * a fetch agent reset.
+ * Check to see if the dead bit is set. If so, we'll
+ * have to initiate a fetch agent reset.
*/
- if (STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- /*
- * Initiate a fetch agent reset.
- */
- SBP2_DEBUG("Dead bit set - initiating fetch agent reset");
+ if (STATUS_TEST_DEAD(h)) {
+ SBP2_DEBUG("Dead bit set - "
+ "initiating fetch agent reset");
sbp2_agent_reset(scsi_id, 0);
}
-
SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb);
}
/*
- * Check here to see if there are no commands in-use. If there are none, we can
- * null out last orb so that next time around we write directly to the orb pointer...
- * Quick start saves one 1394 bus transaction.
+ * Check here to see if there are no commands in-use. If there
+ * are none, we know that the fetch agent left the active state
+ * _and_ that we did not reactivate it yet. Therefore clear
+ * last_orb so that next time we write directly to the
+ * ORB_POINTER register. That way the fetch agent does not need
+ * to refetch the next_ORB.
*/
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
- if (list_empty(&scsi_id->sbp2_command_orb_inuse)) {
+ if (list_empty(&scsi_id->sbp2_command_orb_inuse))
scsi_id->last_orb = NULL;
- }
spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
} else {
-
/*
* It's probably a login/logout/reconnect status.
*/
- if ((scsi_id->login_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
- (scsi_id->query_logins_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
- (scsi_id->reconnect_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
- (scsi_id->logout_orb_dma == scsi_id->status_block.ORB_offset_lo)) {
- atomic_set(&scsi_id->sbp2_login_complete, 1);
+ if ((sb->ORB_offset_lo == scsi_id->reconnect_orb_dma) ||
+ (sb->ORB_offset_lo == scsi_id->login_orb_dma) ||
+ (sb->ORB_offset_lo == scsi_id->query_logins_orb_dma) ||
+ (sb->ORB_offset_lo == scsi_id->logout_orb_dma)) {
+ scsi_id->access_complete = 1;
+ wake_up_interruptible(&access_wq);
}
}
if (SCpnt) {
-
- /* Complete the SCSI command. */
SBP2_DEBUG("Completing SCSI command");
sbp2scsi_complete_command(scsi_id, scsi_status, SCpnt,
command->Current_done);
@@ -2372,7 +2392,7 @@ static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id
command = list_entry(lh, struct sbp2_command_info, list);
pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
PCI_DMA_BIDIRECTIONAL);
@@ -2495,6 +2515,7 @@ static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
(struct scsi_id_instance_data *)sdev->host->hostdata[0];
scsi_id->sdev = sdev;
+ sdev->allow_restart = 1;
if (scsi_id->workarounds & SBP2_WORKAROUND_INQUIRY_36)
sdev->inquiry_len = 36;
@@ -2508,16 +2529,12 @@ static int sbp2scsi_slave_configure(struct scsi_device *sdev)
blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
sdev->use_10_for_rw = 1;
- sdev->use_10_for_ms = 1;
if (sdev->type == TYPE_DISK &&
scsi_id->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
sdev->skip_ms_page_8 = 1;
if (scsi_id->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
sdev->fix_capacity = 1;
- if (scsi_id->ne->guid_vendor_id == 0x0010b9 && /* Maxtor's OUI */
- (sdev->type == TYPE_DISK || sdev->type == TYPE_RBC))
- sdev->allow_restart = 1;
return 0;
}
@@ -2555,7 +2572,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
pci_dma_sync_single_for_cpu(hi->host->pdev,
command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_cpu(hi->host->pdev,
command->sge_dma,
sizeof(command->scatter_gather_element),
@@ -2571,7 +2588,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
/*
* Initiate a fetch agent reset.
*/
- sbp2_agent_reset(scsi_id, 0);
+ sbp2_agent_reset(scsi_id, 1);
sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
}
@@ -2590,7 +2607,7 @@ static int sbp2scsi_reset(struct scsi_cmnd *SCpnt)
if (sbp2util_node_is_available(scsi_id)) {
SBP2_ERR("Generating sbp2 fetch agent reset");
- sbp2_agent_reset(scsi_id, 0);
+ sbp2_agent_reset(scsi_id, 1);
}
return SUCCESS;
diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h
index b22ce1aa8fe..abbe48e646c 100644
--- a/drivers/ieee1394/sbp2.h
+++ b/drivers/ieee1394/sbp2.h
@@ -46,8 +46,8 @@
#define ORB_SET_DIRECTION(value) ((value & 0x1) << 27)
struct sbp2_command_orb {
- volatile u32 next_ORB_hi;
- volatile u32 next_ORB_lo;
+ u32 next_ORB_hi;
+ u32 next_ORB_lo;
u32 data_descriptor_hi;
u32 data_descriptor_lo;
u32 misc;
@@ -180,12 +180,14 @@ struct sbp2_unrestricted_page_table {
#define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff
-#define STATUS_GET_ORB_OFFSET_HI(value) (value & 0xffff)
-#define STATUS_GET_SBP_STATUS(value) ((value >> 16) & 0xff)
-#define STATUS_GET_LENGTH(value) ((value >> 24) & 0x7)
-#define STATUS_GET_DEAD_BIT(value) ((value >> 27) & 0x1)
-#define STATUS_GET_RESP(value) ((value >> 28) & 0x3)
-#define STATUS_GET_SRC(value) ((value >> 30) & 0x3)
+#define STATUS_GET_SRC(value) (((value) >> 30) & 0x3)
+#define STATUS_GET_RESP(value) (((value) >> 28) & 0x3)
+#define STATUS_GET_LEN(value) (((value) >> 24) & 0x7)
+#define STATUS_GET_SBP_STATUS(value) (((value) >> 16) & 0xff)
+#define STATUS_GET_ORB_OFFSET_HI(value) ((value) & 0x0000ffff)
+#define STATUS_TEST_DEAD(value) ((value) & 0x08000000)
+/* test 'resp' | 'dead' | 'sbp2_status' */
+#define STATUS_TEST_RDS(value) ((value) & 0x38ff0000)
struct sbp2_status_block {
u32 ORB_offset_hi_misc;
@@ -318,9 +320,9 @@ struct scsi_id_instance_data {
u64 status_fifo_addr;
/*
- * Variable used for logins, reconnects, logouts, query logins
+ * Waitqueue flag for logins, reconnects, logouts, query logins
*/
- atomic_t sbp2_login_complete;
+ int access_complete:1;
/*
* Pool of command orbs, so we can have more than overlapped command per id
@@ -344,6 +346,16 @@ struct scsi_id_instance_data {
/* Device specific workarounds/brokeness */
unsigned workarounds;
+
+ atomic_t state;
+ struct work_struct protocol_work;
+};
+
+/* For use in scsi_id_instance_data.state */
+enum sbp2lu_state_types {
+ SBP2LU_STATE_RUNNING, /* all normal */
+ SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */
+ SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */
};
/* Sbp2 host data structure (one per IEEE1394 host) */
@@ -390,11 +402,6 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id);
static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid,
quadlet_t *data, u64 addr, size_t length, u16 flags);
static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait);
-static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
- struct sbp2_command_info *command);
-static int sbp2_send_command(struct scsi_id_instance_data *scsi_id,
- struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *));
static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status,
unchar *sense_data);
static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id,
diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c
index c6e3f02bc6d..9bc65059cc6 100644
--- a/drivers/ieee1394/video1394.c
+++ b/drivers/ieee1394/video1394.c
@@ -49,16 +49,16 @@
#include <linux/compat.h>
#include <linux/cdev.h>
-#include "ieee1394.h"
-#include "ieee1394_types.h"
+#include "dma.h"
+#include "highlevel.h"
#include "hosts.h"
+#include "ieee1394.h"
#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "video1394.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
#include "nodemgr.h"
-#include "dma.h"
-
#include "ohci1394.h"
+#include "video1394.h"
#define ISO_CHANNELS 64
@@ -129,7 +129,7 @@ struct file_ctx {
#define DBGMSG(card, fmt, args...) \
printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args)
#else
-#define DBGMSG(card, fmt, args...)
+#define DBGMSG(card, fmt, args...) do {} while (0)
#endif
/* print general (card independent) information */
@@ -1181,7 +1181,8 @@ static int video1394_mmap(struct file *file, struct vm_area_struct *vma)
lock_kernel();
if (ctx->current_ctx == NULL) {
- PRINT(KERN_ERR, ctx->ohci->host->id, "Current iso context not set");
+ PRINT(KERN_ERR, ctx->ohci->host->id,
+ "Current iso context not set");
} else
res = dma_region_mmap(&ctx->current_ctx->dma, file, vma);
unlock_kernel();
@@ -1189,6 +1190,40 @@ static int video1394_mmap(struct file *file, struct vm_area_struct *vma)
return res;
}
+static unsigned int video1394_poll(struct file *file, poll_table *pt)
+{
+ struct file_ctx *ctx;
+ unsigned int mask = 0;
+ unsigned long flags;
+ struct dma_iso_ctx *d;
+ int i;
+
+ lock_kernel();
+ ctx = file->private_data;
+ d = ctx->current_ctx;
+ if (d == NULL) {
+ PRINT(KERN_ERR, ctx->ohci->host->id,
+ "Current iso context not set");
+ mask = POLLERR;
+ goto done;
+ }
+
+ poll_wait(file, &d->waitq, pt);
+
+ spin_lock_irqsave(&d->lock, flags);
+ for (i = 0; i < d->num_desc; i++) {
+ if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) {
+ mask |= POLLIN | POLLRDNORM;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&d->lock, flags);
+done:
+ unlock_kernel();
+
+ return mask;
+}
+
static int video1394_open(struct inode *inode, struct file *file)
{
int i = ieee1394_file_to_instance(file);
@@ -1257,6 +1292,7 @@ static struct file_operations video1394_fops=
#ifdef CONFIG_COMPAT
.compat_ioctl = video1394_compat_ioctl,
#endif
+ .poll = video1394_poll,
.mmap = video1394_mmap,
.open = video1394_open,
.release = video1394_release
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 9cbf09e2052..60d3fbdd216 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -86,7 +86,7 @@ EXPORT_SYMBOL(rdma_copy_addr);
int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
{
struct net_device *dev;
- u32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+ __be32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
int ret;
dev = ip_dev_find(ip);
@@ -239,7 +239,7 @@ static int addr_resolve_local(struct sockaddr_in *src_in,
{
struct net_device *dev;
u32 src_ip = src_in->sin_addr.s_addr;
- u32 dst_ip = dst_in->sin_addr.s_addr;
+ __be32 dst_ip = dst_in->sin_addr.s_addr;
int ret;
dev = ip_dev_find(dst_ip);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 082f03c158f..493f4c65c7a 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -2987,10 +2987,7 @@ error1:
static void __exit ib_mad_cleanup_module(void)
{
ib_unregister_client(&mad_client);
-
- if (kmem_cache_destroy(ib_mad_cache)) {
- printk(KERN_DEBUG PFX "Failed to destroy ib_mad cache\n");
- }
+ kmem_cache_destroy(ib_mad_cache);
}
module_init(ib_mad_init_module);
diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c
index 08f46c83a3a..3aae4978e1c 100644
--- a/drivers/infiniband/hw/amso1100/c2_ae.c
+++ b/drivers/infiniband/hw/amso1100/c2_ae.c
@@ -197,7 +197,7 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index)
"resource=%x, qp_state=%s\n",
__FUNCTION__,
to_event_str(event_id),
- be64_to_cpu(wr->ae.ae_generic.user_context),
+ (unsigned long long) be64_to_cpu(wr->ae.ae_generic.user_context),
be32_to_cpu(wr->ae.ae_generic.resource_type),
be32_to_cpu(wr->ae.ae_generic.resource),
to_qp_state_str(be32_to_cpu(wr->ae.ae_generic.qp_state)));
diff --git a/drivers/infiniband/hw/amso1100/c2_alloc.c b/drivers/infiniband/hw/amso1100/c2_alloc.c
index 1d2529992c0..028a60bbfca 100644
--- a/drivers/infiniband/hw/amso1100/c2_alloc.c
+++ b/drivers/infiniband/hw/amso1100/c2_alloc.c
@@ -115,7 +115,7 @@ u16 *c2_alloc_mqsp(struct c2_dev *c2dev, struct sp_chunk *head,
((unsigned long) &(head->shared_ptr[mqsp]) -
(unsigned long) head);
pr_debug("%s addr %p dma_addr %llx\n", __FUNCTION__,
- &(head->shared_ptr[mqsp]), (u64)*dma_addr);
+ &(head->shared_ptr[mqsp]), (unsigned long long) *dma_addr);
return &(head->shared_ptr[mqsp]);
}
return NULL;
diff --git a/drivers/infiniband/hw/amso1100/c2_cm.c b/drivers/infiniband/hw/amso1100/c2_cm.c
index 485254efdd1..75b93e9b881 100644
--- a/drivers/infiniband/hw/amso1100/c2_cm.c
+++ b/drivers/infiniband/hw/amso1100/c2_cm.c
@@ -302,7 +302,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
vq_req = vq_req_alloc(c2dev);
if (!vq_req) {
err = -ENOMEM;
- goto bail1;
+ goto bail0;
}
vq_req->qp = qp;
vq_req->cm_id = cm_id;
@@ -311,7 +311,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
if (!wr) {
err = -ENOMEM;
- goto bail2;
+ goto bail1;
}
/* Build the WR */
@@ -331,7 +331,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
/* Validate private_data length */
if (iw_param->private_data_len > C2_MAX_PRIVATE_DATA_SIZE) {
err = -EINVAL;
- goto bail2;
+ goto bail1;
}
if (iw_param->private_data) {
@@ -348,19 +348,19 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
err = vq_send_wr(c2dev, (union c2wr *) wr);
if (err) {
vq_req_put(c2dev, vq_req);
- goto bail2;
+ goto bail1;
}
/* Wait for reply from adapter */
err = vq_wait_for_reply(c2dev, vq_req);
if (err)
- goto bail2;
+ goto bail1;
/* Check that reply is present */
reply = (struct c2wr_cr_accept_rep *) (unsigned long) vq_req->reply_msg;
if (!reply) {
err = -ENOMEM;
- goto bail2;
+ goto bail1;
}
err = c2_errno(reply);
@@ -368,9 +368,8 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
if (!err)
c2_set_qp_state(qp, C2_QP_STATE_RTS);
- bail2:
- kfree(wr);
bail1:
+ kfree(wr);
vq_req_free(c2dev, vq_req);
bail0:
if (err) {
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index dd6af551108..da98d9f7142 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -390,14 +390,18 @@ static struct ib_mr *c2_reg_phys_mr(struct ib_pd *ib_pd,
}
mr = kmalloc(sizeof(*mr), GFP_KERNEL);
- if (!mr)
+ if (!mr) {
+ vfree(page_list);
return ERR_PTR(-ENOMEM);
+ }
mr->pd = to_c2pd(ib_pd);
pr_debug("%s - page shift %d, pbl_depth %d, total_len %u, "
"*iova_start %llx, first pa %llx, last pa %llx\n",
__FUNCTION__, page_shift, pbl_depth, total_len,
- *iova_start, page_list[0], page_list[pbl_depth-1]);
+ (unsigned long long) *iova_start,
+ (unsigned long long) page_list[0],
+ (unsigned long long) page_list[pbl_depth-1]);
err = c2_nsmr_register_phys_kern(to_c2dev(ib_pd->device), page_list,
(1 << page_shift), pbl_depth,
total_len, 0, iova_start,
diff --git a/drivers/infiniband/hw/amso1100/c2_rnic.c b/drivers/infiniband/hw/amso1100/c2_rnic.c
index f49a32b7a8f..e37c5688c21 100644
--- a/drivers/infiniband/hw/amso1100/c2_rnic.c
+++ b/drivers/infiniband/hw/amso1100/c2_rnic.c
@@ -527,7 +527,7 @@ int c2_rnic_init(struct c2_dev *c2dev)
DMA_FROM_DEVICE);
pci_unmap_addr_set(&c2dev->rep_vq, mapping, c2dev->rep_vq.host_dma);
pr_debug("%s rep_vq va %p dma %llx\n", __FUNCTION__, q1_pages,
- (u64)c2dev->rep_vq.host_dma);
+ (unsigned long long) c2dev->rep_vq.host_dma);
c2_mq_rep_init(&c2dev->rep_vq,
1,
qsize,
@@ -550,7 +550,7 @@ int c2_rnic_init(struct c2_dev *c2dev)
DMA_FROM_DEVICE);
pci_unmap_addr_set(&c2dev->aeq, mapping, c2dev->aeq.host_dma);
pr_debug("%s aeq va %p dma %llx\n", __FUNCTION__, q1_pages,
- (u64)c2dev->rep_vq.host_dma);
+ (unsigned long long) c2dev->rep_vq.host_dma);
c2_mq_rep_init(&c2dev->aeq,
2,
qsize,
diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h
index f577905e3ac..54139d39818 100644
--- a/drivers/infiniband/hw/ipath/ipath_common.h
+++ b/drivers/infiniband/hw/ipath/ipath_common.h
@@ -141,8 +141,9 @@ struct infinipath_stats {
* packets if ipath not configured, etc.)
*/
__u64 sps_krdrops;
+ __u64 sps_txeparity; /* PIO buffer parity error, recovered */
/* pad for future growth */
- __u64 __sps_pad[46];
+ __u64 __sps_pad[45];
};
/*
@@ -185,6 +186,9 @@ typedef enum _ipath_ureg {
#define IPATH_RUNTIME_PCIE 0x2
#define IPATH_RUNTIME_FORCE_WC_ORDER 0x4
#define IPATH_RUNTIME_RCVHDR_COPY 0x8
+#define IPATH_RUNTIME_MASTER 0x10
+#define IPATH_RUNTIME_PBC_REWRITE 0x20
+#define IPATH_RUNTIME_LOOSE_DMA_ALIGN 0x40
/*
* This structure is returned by ipath_userinit() immediately after
@@ -202,7 +206,8 @@ struct ipath_base_info {
/* version of software, for feature checking. */
__u32 spi_sw_version;
/* InfiniPath port assigned, goes into sent packets */
- __u32 spi_port;
+ __u16 spi_port;
+ __u16 spi_subport;
/*
* IB MTU, packets IB data must be less than this.
* The MTU is in bytes, and will be a multiple of 4 bytes.
@@ -218,7 +223,7 @@ struct ipath_base_info {
__u32 spi_tidcnt;
/* size of the TID Eager list in infinipath, in entries */
__u32 spi_tidegrcnt;
- /* size of a single receive header queue entry. */
+ /* size of a single receive header queue entry in words. */
__u32 spi_rcvhdrent_size;
/*
* Count of receive header queue entries allocated.
@@ -310,6 +315,12 @@ struct ipath_base_info {
__u32 spi_filler_for_align;
/* address of readonly memory copy of the rcvhdrq tail register. */
__u64 spi_rcvhdr_tailaddr;
+
+ /* shared memory pages for subports if IPATH_RUNTIME_MASTER is set */
+ __u64 spi_subport_uregbase;
+ __u64 spi_subport_rcvegrbuf;
+ __u64 spi_subport_rcvhdr_base;
+
} __attribute__ ((aligned(8)));
@@ -328,12 +339,12 @@ struct ipath_base_info {
/*
* Minor version differences are always compatible
- * a within a major version, however if if user software is larger
+ * a within a major version, however if user software is larger
* than driver software, some new features and/or structure fields
* may not be implemented; the user code must deal with this if it
- * cares, or it must abort after initialization reports the difference
+ * cares, or it must abort after initialization reports the difference.
*/
-#define IPATH_USER_SWMINOR 2
+#define IPATH_USER_SWMINOR 3
#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR)
@@ -379,7 +390,16 @@ struct ipath_user_info {
*/
__u32 spu_rcvhdrsize;
- __u64 spu_unused; /* kept for compatible layout */
+ /*
+ * If two or more processes wish to share a port, each process
+ * must set the spu_subport_cnt and spu_subport_id to the same
+ * values. The only restriction on the spu_subport_id is that
+ * it be unique for a given node.
+ */
+ __u16 spu_subport_cnt;
+ __u16 spu_subport_id;
+
+ __u32 spu_unused; /* kept for compatible layout */
/*
* address of struct base_info to write to
@@ -392,19 +412,25 @@ struct ipath_user_info {
#define IPATH_CMD_MIN 16
-#define IPATH_CMD_USER_INIT 16 /* set up userspace */
+#define __IPATH_CMD_USER_INIT 16 /* old set up userspace (for old user code) */
#define IPATH_CMD_PORT_INFO 17 /* find out what resources we got */
#define IPATH_CMD_RECV_CTRL 18 /* control receipt of packets */
#define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */
#define IPATH_CMD_TID_FREE 20 /* free expected TID entries */
#define IPATH_CMD_SET_PART_KEY 21 /* add partition key */
+#define IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes */
+#define IPATH_CMD_ASSIGN_PORT 23 /* allocate HCA and port */
+#define IPATH_CMD_USER_INIT 24 /* set up userspace */
-#define IPATH_CMD_MAX 21
+#define IPATH_CMD_MAX 24
struct ipath_port_info {
__u32 num_active; /* number of active units */
__u32 unit; /* unit (chip) assigned to caller */
- __u32 port; /* port on unit assigned to caller */
+ __u16 port; /* port on unit assigned to caller */
+ __u16 subport; /* subport on unit assigned to caller */
+ __u16 num_ports; /* number of ports available on unit */
+ __u16 num_subports; /* number of subport slaves opened on port */
};
struct ipath_tid_info {
@@ -435,6 +461,8 @@ struct ipath_cmd {
__u32 recv_ctrl;
/* partition key to set */
__u16 part_key;
+ /* user address of __u32 bitmask of active slaves */
+ __u64 slave_mask_addr;
} cmd;
};
@@ -596,6 +624,10 @@ struct infinipath_counters {
/* K_PktFlags bits */
#define INFINIPATH_KPF_INTR 0x1
+#define INFINIPATH_KPF_SUBPORT_MASK 0x3
+#define INFINIPATH_KPF_SUBPORT_SHIFT 1
+
+#define INFINIPATH_MAX_SUBPORT 4
/* SendPIO per-buffer control */
#define INFINIPATH_SP_TEST 0x40
@@ -610,7 +642,7 @@ struct ipath_header {
/*
* Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset -
* 14 bits before ECO change ~28 Dec 03. After that, Vers 4,
- * Port 3, TID 11, offset 14.
+ * Port 4, TID 11, offset 13.
*/
__le32 ver_port_tid_offset;
__le16 chksum;
diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c
index 049221bc590..87462e0cb4d 100644
--- a/drivers/infiniband/hw/ipath/ipath_cq.c
+++ b/drivers/infiniband/hw/ipath/ipath_cq.c
@@ -46,7 +46,7 @@
*/
void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
{
- struct ipath_cq_wc *wc = cq->queue;
+ struct ipath_cq_wc *wc;
unsigned long flags;
u32 head;
u32 next;
@@ -57,6 +57,7 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
* Note that the head pointer might be writable by user processes.
* Take care to verify it is a sane value.
*/
+ wc = cq->queue;
head = wc->head;
if (head >= (unsigned) cq->ibcq.cqe) {
head = cq->ibcq.cqe;
@@ -109,21 +110,27 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
{
struct ipath_cq *cq = to_icq(ibcq);
- struct ipath_cq_wc *wc = cq->queue;
+ struct ipath_cq_wc *wc;
unsigned long flags;
int npolled;
+ u32 tail;
spin_lock_irqsave(&cq->lock, flags);
+ wc = cq->queue;
+ tail = wc->tail;
+ if (tail > (u32) cq->ibcq.cqe)
+ tail = (u32) cq->ibcq.cqe;
for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
- if (wc->tail == wc->head)
+ if (tail == wc->head)
break;
- *entry = wc->queue[wc->tail];
- if (wc->tail >= cq->ibcq.cqe)
- wc->tail = 0;
+ *entry = wc->queue[tail];
+ if (tail >= cq->ibcq.cqe)
+ tail = 0;
else
- wc->tail++;
+ tail++;
}
+ wc->tail = tail;
spin_unlock_irqrestore(&cq->lock, flags);
@@ -177,11 +184,6 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
goto done;
}
- if (dev->n_cqs_allocated == ib_ipath_max_cqs) {
- ret = ERR_PTR(-ENOMEM);
- goto done;
- }
-
/* Allocate the completion queue structure. */
cq = kmalloc(sizeof(*cq), GFP_KERNEL);
if (!cq) {
@@ -237,6 +239,16 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
} else
cq->ip = NULL;
+ spin_lock(&dev->n_cqs_lock);
+ if (dev->n_cqs_allocated == ib_ipath_max_cqs) {
+ spin_unlock(&dev->n_cqs_lock);
+ ret = ERR_PTR(-ENOMEM);
+ goto bail_wc;
+ }
+
+ dev->n_cqs_allocated++;
+ spin_unlock(&dev->n_cqs_lock);
+
/*
* ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe.
* The number of entries should be >= the number requested or return
@@ -253,7 +265,6 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
ret = &cq->ibcq;
- dev->n_cqs_allocated++;
goto done;
bail_wc:
@@ -280,7 +291,9 @@ int ipath_destroy_cq(struct ib_cq *ibcq)
struct ipath_cq *cq = to_icq(ibcq);
tasklet_kill(&cq->comptask);
+ spin_lock(&dev->n_cqs_lock);
dev->n_cqs_allocated--;
+ spin_unlock(&dev->n_cqs_lock);
if (cq->ip)
kref_put(&cq->ip->ref, ipath_release_mmap_info);
else
@@ -316,10 +329,16 @@ int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify notify)
return 0;
}
+/**
+ * ipath_resize_cq - change the size of the CQ
+ * @ibcq: the completion queue
+ *
+ * Returns 0 for success.
+ */
int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
{
struct ipath_cq *cq = to_icq(ibcq);
- struct ipath_cq_wc *old_wc = cq->queue;
+ struct ipath_cq_wc *old_wc;
struct ipath_cq_wc *wc;
u32 head, tail, n;
int ret;
@@ -355,6 +374,7 @@ int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
* Make sure head and tail are sane since they
* might be user writable.
*/
+ old_wc = cq->queue;
head = old_wc->head;
if (head > (u32) cq->ibcq.cqe)
head = (u32) cq->ibcq.cqe;
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index 2108466c7e3..12cefa658f3 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -95,16 +95,6 @@ const char *ipath_ibcstatus_str[] = {
"RecovIdle",
};
-/*
- * These variables are initialized in the chip-specific files
- * but are defined here.
- */
-u16 ipath_gpio_sda_num, ipath_gpio_scl_num;
-u64 ipath_gpio_sda, ipath_gpio_scl;
-u64 infinipath_i_bitsextant;
-ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
-u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;
-
static void __devexit ipath_remove_one(struct pci_dev *);
static int __devinit ipath_init_one(struct pci_dev *,
const struct pci_device_id *);
@@ -527,28 +517,146 @@ bail:
return ret;
}
+static void __devexit cleanup_device(struct ipath_devdata *dd)
+{
+ int port;
+
+ ipath_shutdown_device(dd);
+
+ if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) {
+ /* can't do anything more with chip; needs re-init */
+ *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT;
+ if (dd->ipath_kregbase) {
+ /*
+ * if we haven't already cleaned up before these are
+ * to ensure any register reads/writes "fail" until
+ * re-init
+ */
+ dd->ipath_kregbase = NULL;
+ dd->ipath_uregbase = 0;
+ dd->ipath_sregbase = 0;
+ dd->ipath_cregbase = 0;
+ dd->ipath_kregsize = 0;
+ }
+ ipath_disable_wc(dd);
+ }
+
+ if (dd->ipath_pioavailregs_dma) {
+ dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
+ (void *) dd->ipath_pioavailregs_dma,
+ dd->ipath_pioavailregs_phys);
+ dd->ipath_pioavailregs_dma = NULL;
+ }
+ if (dd->ipath_dummy_hdrq) {
+ dma_free_coherent(&dd->pcidev->dev,
+ dd->ipath_pd[0]->port_rcvhdrq_size,
+ dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys);
+ dd->ipath_dummy_hdrq = NULL;
+ }
+
+ if (dd->ipath_pageshadow) {
+ struct page **tmpp = dd->ipath_pageshadow;
+ dma_addr_t *tmpd = dd->ipath_physshadow;
+ int i, cnt = 0;
+
+ ipath_cdbg(VERBOSE, "Unlocking any expTID pages still "
+ "locked\n");
+ for (port = 0; port < dd->ipath_cfgports; port++) {
+ int port_tidbase = port * dd->ipath_rcvtidcnt;
+ int maxtid = port_tidbase + dd->ipath_rcvtidcnt;
+ for (i = port_tidbase; i < maxtid; i++) {
+ if (!tmpp[i])
+ continue;
+ pci_unmap_page(dd->pcidev, tmpd[i],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
+ ipath_release_user_pages(&tmpp[i], 1);
+ tmpp[i] = NULL;
+ cnt++;
+ }
+ }
+ if (cnt) {
+ ipath_stats.sps_pageunlocks += cnt;
+ ipath_cdbg(VERBOSE, "There were still %u expTID "
+ "entries locked\n", cnt);
+ }
+ if (ipath_stats.sps_pagelocks ||
+ ipath_stats.sps_pageunlocks)
+ ipath_cdbg(VERBOSE, "%llu pages locked, %llu "
+ "unlocked via ipath_m{un}lock\n",
+ (unsigned long long)
+ ipath_stats.sps_pagelocks,
+ (unsigned long long)
+ ipath_stats.sps_pageunlocks);
+
+ ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n",
+ dd->ipath_pageshadow);
+ vfree(dd->ipath_pageshadow);
+ dd->ipath_pageshadow = NULL;
+ }
+
+ /*
+ * free any resources still in use (usually just kernel ports)
+ * at unload; we do for portcnt, not cfgports, because cfgports
+ * could have changed while we were loaded.
+ */
+ for (port = 0; port < dd->ipath_portcnt; port++) {
+ struct ipath_portdata *pd = dd->ipath_pd[port];
+ dd->ipath_pd[port] = NULL;
+ ipath_free_pddata(dd, pd);
+ }
+ kfree(dd->ipath_pd);
+ /*
+ * debuggability, in case some cleanup path tries to use it
+ * after this
+ */
+ dd->ipath_pd = NULL;
+}
+
static void __devexit ipath_remove_one(struct pci_dev *pdev)
{
- struct ipath_devdata *dd;
+ struct ipath_devdata *dd = pci_get_drvdata(pdev);
- ipath_cdbg(VERBOSE, "removing, pdev=%p\n", pdev);
- if (!pdev)
- return;
+ ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd);
+
+ if (dd->verbs_dev)
+ ipath_unregister_ib_device(dd->verbs_dev);
- dd = pci_get_drvdata(pdev);
- ipath_unregister_ib_device(dd->verbs_dev);
ipath_diag_remove(dd);
ipath_user_remove(dd);
ipathfs_remove_device(dd);
ipath_device_remove_group(&pdev->dev, dd);
+
ipath_cdbg(VERBOSE, "Releasing pci memory regions, dd %p, "
"unit %u\n", dd, (u32) dd->ipath_unit);
- if (dd->ipath_kregbase) {
- ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n",
- dd->ipath_kregbase);
- iounmap((volatile void __iomem *) dd->ipath_kregbase);
- dd->ipath_kregbase = NULL;
- }
+
+ cleanup_device(dd);
+
+ /*
+ * turn off rcv, send, and interrupts for all ports, all drivers
+ * should also hard reset the chip here?
+ * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs
+ * for all versions of the driver, if they were allocated
+ */
+ if (pdev->irq) {
+ ipath_cdbg(VERBOSE,
+ "unit %u free_irq of irq %x\n",
+ dd->ipath_unit, pdev->irq);
+ free_irq(pdev->irq, dd);
+ } else
+ ipath_dbg("irq is 0, not doing free_irq "
+ "for unit %u\n", dd->ipath_unit);
+ /*
+ * we check for NULL here, because it's outside
+ * the kregbase check, and we need to call it
+ * after the free_irq. Thus it's possible that
+ * the function pointers were never initialized.
+ */
+ if (dd->ipath_f_cleanup)
+ /* clean up chip-specific stuff */
+ dd->ipath_f_cleanup(dd);
+
+ ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n", dd->ipath_kregbase);
+ iounmap((volatile void __iomem *) dd->ipath_kregbase);
pci_release_regions(pdev);
ipath_cdbg(VERBOSE, "calling pci_disable_device\n");
pci_disable_device(pdev);
@@ -760,8 +868,8 @@ static void get_rhf_errstring(u32 err, char *msg, size_t len)
static inline void *ipath_get_egrbuf(struct ipath_devdata *dd, u32 bufnum,
int err)
{
- return dd->ipath_port0_skbs ?
- (void *)dd->ipath_port0_skbs[bufnum]->data : NULL;
+ return dd->ipath_port0_skbinfo ?
+ (void *) dd->ipath_port0_skbinfo[bufnum].skb->data : NULL;
}
/**
@@ -783,31 +891,34 @@ struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd,
*/
/*
- * We need 4 extra bytes for unaligned transfer copying
+ * We need 2 extra bytes for ipath_ether data sent in the
+ * key header. In order to keep everything dword aligned,
+ * we'll reserve 4 bytes.
*/
+ len = dd->ipath_ibmaxlen + 4;
+
if (dd->ipath_flags & IPATH_4BYTE_TID) {
- /* we need a 4KB multiple alignment, and there is no way
+ /* We need a 2KB multiple alignment, and there is no way
* to do it except to allocate extra and then skb_reserve
* enough to bring it up to the right alignment.
*/
- len = dd->ipath_ibmaxlen + 4 + (1 << 11) - 1;
+ len += 2047;
}
- else
- len = dd->ipath_ibmaxlen + 4;
+
skb = __dev_alloc_skb(len, gfp_mask);
if (!skb) {
ipath_dev_err(dd, "Failed to allocate skbuff, length %u\n",
len);
goto bail;
}
+
+ skb_reserve(skb, 4);
+
if (dd->ipath_flags & IPATH_4BYTE_TID) {
- u32 una = ((1 << 11) - 1) & (unsigned long)(skb->data + 4);
+ u32 una = (unsigned long)skb->data & 2047;
if (una)
- skb_reserve(skb, 4 + (1 << 11) - una);
- else
- skb_reserve(skb, 4);
- } else
- skb_reserve(skb, 4);
+ skb_reserve(skb, 2048 - una);
+ }
bail:
return skb;
@@ -1326,6 +1437,9 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd,
"for port %u rcvhdrqtailaddr failed\n",
pd->port_port);
ret = -ENOMEM;
+ dma_free_coherent(&dd->pcidev->dev, amt,
+ pd->port_rcvhdrq, pd->port_rcvhdrq_phys);
+ pd->port_rcvhdrq = NULL;
goto bail;
}
pd->port_rcvhdrqtailaddr_phys = phys_hdrqtail;
@@ -1347,12 +1461,13 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd,
ipath_cdbg(VERBOSE, "reuse port %d rcvhdrq @%p %llx phys; "
"hdrtailaddr@%p %llx physical\n",
pd->port_port, pd->port_rcvhdrq,
- pd->port_rcvhdrq_phys, pd->port_rcvhdrtail_kvaddr,
- (unsigned long long)pd->port_rcvhdrqtailaddr_phys);
+ (unsigned long long) pd->port_rcvhdrq_phys,
+ pd->port_rcvhdrtail_kvaddr, (unsigned long long)
+ pd->port_rcvhdrqtailaddr_phys);
/* clear for security and sanity on each use */
memset(pd->port_rcvhdrq, 0, pd->port_rcvhdrq_size);
- memset((void *)pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE);
+ memset(pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE);
/*
* tell chip each time we init it, even if we are re-using previous
@@ -1805,7 +1920,7 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd)
pd->port_rcvhdrq = NULL;
if (pd->port_rcvhdrtail_kvaddr) {
dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
- (void *)pd->port_rcvhdrtail_kvaddr,
+ pd->port_rcvhdrtail_kvaddr,
pd->port_rcvhdrqtailaddr_phys);
pd->port_rcvhdrtail_kvaddr = NULL;
}
@@ -1824,24 +1939,32 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd)
dma_free_coherent(&dd->pcidev->dev, size,
base, pd->port_rcvegrbuf_phys[e]);
}
- vfree(pd->port_rcvegrbuf);
+ kfree(pd->port_rcvegrbuf);
pd->port_rcvegrbuf = NULL;
- vfree(pd->port_rcvegrbuf_phys);
+ kfree(pd->port_rcvegrbuf_phys);
pd->port_rcvegrbuf_phys = NULL;
pd->port_rcvegrbuf_chunks = 0;
- } else if (pd->port_port == 0 && dd->ipath_port0_skbs) {
+ } else if (pd->port_port == 0 && dd->ipath_port0_skbinfo) {
unsigned e;
- struct sk_buff **skbs = dd->ipath_port0_skbs;
+ struct ipath_skbinfo *skbinfo = dd->ipath_port0_skbinfo;
- dd->ipath_port0_skbs = NULL;
- ipath_cdbg(VERBOSE, "free closed port %d ipath_port0_skbs "
- "@ %p\n", pd->port_port, skbs);
+ dd->ipath_port0_skbinfo = NULL;
+ ipath_cdbg(VERBOSE, "free closed port %d "
+ "ipath_port0_skbinfo @ %p\n", pd->port_port,
+ skbinfo);
for (e = 0; e < dd->ipath_rcvegrcnt; e++)
- if (skbs[e])
- dev_kfree_skb(skbs[e]);
- vfree(skbs);
+ if (skbinfo[e].skb) {
+ pci_unmap_single(dd->pcidev, skbinfo[e].phys,
+ dd->ipath_ibmaxlen,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(skbinfo[e].skb);
+ }
+ vfree(skbinfo);
}
kfree(pd->port_tid_pg_list);
+ vfree(pd->subport_uregbase);
+ vfree(pd->subport_rcvegrbuf);
+ vfree(pd->subport_rcvhdr_base);
kfree(pd);
}
@@ -1907,150 +2030,12 @@ bail:
return ret;
}
-static void cleanup_device(struct ipath_devdata *dd)
-{
- int port;
-
- ipath_shutdown_device(dd);
-
- if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) {
- /* can't do anything more with chip; needs re-init */
- *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT;
- if (dd->ipath_kregbase) {
- /*
- * if we haven't already cleaned up before these are
- * to ensure any register reads/writes "fail" until
- * re-init
- */
- dd->ipath_kregbase = NULL;
- dd->ipath_uregbase = 0;
- dd->ipath_sregbase = 0;
- dd->ipath_cregbase = 0;
- dd->ipath_kregsize = 0;
- }
- ipath_disable_wc(dd);
- }
-
- if (dd->ipath_pioavailregs_dma) {
- dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
- (void *) dd->ipath_pioavailregs_dma,
- dd->ipath_pioavailregs_phys);
- dd->ipath_pioavailregs_dma = NULL;
- }
- if (dd->ipath_dummy_hdrq) {
- dma_free_coherent(&dd->pcidev->dev,
- dd->ipath_pd[0]->port_rcvhdrq_size,
- dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys);
- dd->ipath_dummy_hdrq = NULL;
- }
-
- if (dd->ipath_pageshadow) {
- struct page **tmpp = dd->ipath_pageshadow;
- int i, cnt = 0;
-
- ipath_cdbg(VERBOSE, "Unlocking any expTID pages still "
- "locked\n");
- for (port = 0; port < dd->ipath_cfgports; port++) {
- int port_tidbase = port * dd->ipath_rcvtidcnt;
- int maxtid = port_tidbase + dd->ipath_rcvtidcnt;
- for (i = port_tidbase; i < maxtid; i++) {
- if (!tmpp[i])
- continue;
- ipath_release_user_pages(&tmpp[i], 1);
- tmpp[i] = NULL;
- cnt++;
- }
- }
- if (cnt) {
- ipath_stats.sps_pageunlocks += cnt;
- ipath_cdbg(VERBOSE, "There were still %u expTID "
- "entries locked\n", cnt);
- }
- if (ipath_stats.sps_pagelocks ||
- ipath_stats.sps_pageunlocks)
- ipath_cdbg(VERBOSE, "%llu pages locked, %llu "
- "unlocked via ipath_m{un}lock\n",
- (unsigned long long)
- ipath_stats.sps_pagelocks,
- (unsigned long long)
- ipath_stats.sps_pageunlocks);
-
- ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n",
- dd->ipath_pageshadow);
- vfree(dd->ipath_pageshadow);
- dd->ipath_pageshadow = NULL;
- }
-
- /*
- * free any resources still in use (usually just kernel ports)
- * at unload; we do for portcnt, not cfgports, because cfgports
- * could have changed while we were loaded.
- */
- for (port = 0; port < dd->ipath_portcnt; port++) {
- struct ipath_portdata *pd = dd->ipath_pd[port];
- dd->ipath_pd[port] = NULL;
- ipath_free_pddata(dd, pd);
- }
- kfree(dd->ipath_pd);
- /*
- * debuggability, in case some cleanup path tries to use it
- * after this
- */
- dd->ipath_pd = NULL;
-}
-
static void __exit infinipath_cleanup(void)
{
- struct ipath_devdata *dd, *tmp;
- unsigned long flags;
-
- ipath_diagpkt_remove();
-
ipath_exit_ipathfs();
ipath_driver_remove_group(&ipath_driver.driver);
- spin_lock_irqsave(&ipath_devs_lock, flags);
-
- /*
- * turn off rcv, send, and interrupts for all ports, all drivers
- * should also hard reset the chip here?
- * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs
- * for all versions of the driver, if they were allocated
- */
- list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
- spin_unlock_irqrestore(&ipath_devs_lock, flags);
-
- if (dd->ipath_kregbase)
- cleanup_device(dd);
-
- if (dd->pcidev) {
- if (dd->pcidev->irq) {
- ipath_cdbg(VERBOSE,
- "unit %u free_irq of irq %x\n",
- dd->ipath_unit, dd->pcidev->irq);
- free_irq(dd->pcidev->irq, dd);
- } else
- ipath_dbg("irq is 0, not doing free_irq "
- "for unit %u\n", dd->ipath_unit);
-
- /*
- * we check for NULL here, because it's outside
- * the kregbase check, and we need to call it
- * after the free_irq. Thus it's possible that
- * the function pointers were never initialized.
- */
- if (dd->ipath_f_cleanup)
- /* clean up chip-specific stuff */
- dd->ipath_f_cleanup(dd);
-
- dd->pcidev = NULL;
- }
- spin_lock_irqsave(&ipath_devs_lock, flags);
- }
-
- spin_unlock_irqrestore(&ipath_devs_lock, flags);
-
ipath_cdbg(VERBOSE, "Unregistering pci driver\n");
pci_unregister_driver(&ipath_driver);
diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c
index 3313356ab93..a4019a6b756 100644
--- a/drivers/infiniband/hw/ipath/ipath_eeprom.c
+++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c
@@ -100,9 +100,9 @@ static int i2c_gpio_set(struct ipath_devdata *dd,
gpioval = &dd->ipath_gpio_out;
read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
if (line == i2c_line_scl)
- mask = ipath_gpio_scl;
+ mask = dd->ipath_gpio_scl;
else
- mask = ipath_gpio_sda;
+ mask = dd->ipath_gpio_sda;
if (new_line_state == i2c_line_high)
/* tri-state the output rather than force high */
@@ -119,12 +119,12 @@ static int i2c_gpio_set(struct ipath_devdata *dd,
write_val = 0x0UL;
if (line == i2c_line_scl) {
- write_val <<= ipath_gpio_scl_num;
- *gpioval = *gpioval & ~(1UL << ipath_gpio_scl_num);
+ write_val <<= dd->ipath_gpio_scl_num;
+ *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_scl_num);
*gpioval |= write_val;
} else {
- write_val <<= ipath_gpio_sda_num;
- *gpioval = *gpioval & ~(1UL << ipath_gpio_sda_num);
+ write_val <<= dd->ipath_gpio_sda_num;
+ *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_sda_num);
*gpioval |= write_val;
}
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval);
@@ -157,9 +157,9 @@ static int i2c_gpio_get(struct ipath_devdata *dd,
read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
/* config line to be an input */
if (line == i2c_line_scl)
- mask = ipath_gpio_scl;
+ mask = dd->ipath_gpio_scl;
else
- mask = ipath_gpio_sda;
+ mask = dd->ipath_gpio_sda;
write_val = read_val & ~mask;
ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val);
read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus);
@@ -187,6 +187,7 @@ bail:
static void i2c_wait_for_writes(struct ipath_devdata *dd)
{
(void)ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch);
+ rmb();
}
static void scl_out(struct ipath_devdata *dd, u8 bit)
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 29930e22318..a9ddc6911f6 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -41,6 +41,12 @@
#include "ipath_kernel.h"
#include "ipath_common.h"
+/*
+ * mmap64 doesn't allow all 64 bits for 32-bit applications
+ * so only use the low 43 bits.
+ */
+#define MMAP64_MASK 0x7FFFFFFFFFFUL
+
static int ipath_open(struct inode *, struct file *);
static int ipath_close(struct inode *, struct file *);
static ssize_t ipath_write(struct file *, const char __user *, size_t,
@@ -57,18 +63,35 @@ static struct file_operations ipath_file_ops = {
.mmap = ipath_mmap
};
-static int ipath_get_base_info(struct ipath_portdata *pd,
+static int ipath_get_base_info(struct file *fp,
void __user *ubase, size_t ubase_size)
{
+ struct ipath_portdata *pd = port_fp(fp);
int ret = 0;
struct ipath_base_info *kinfo = NULL;
struct ipath_devdata *dd = pd->port_dd;
+ unsigned subport_cnt;
+ int shared, master;
+ size_t sz;
+
+ subport_cnt = pd->port_subport_cnt;
+ if (!subport_cnt) {
+ shared = 0;
+ master = 0;
+ subport_cnt = 1;
+ } else {
+ shared = 1;
+ master = !subport_fp(fp);
+ }
- if (ubase_size < sizeof(*kinfo)) {
+ sz = sizeof(*kinfo);
+ /* If port sharing is not requested, allow the old size structure */
+ if (!shared)
+ sz -= 3 * sizeof(u64);
+ if (ubase_size < sz) {
ipath_cdbg(PROC,
- "Base size %lu, need %lu (version mismatch?)\n",
- (unsigned long) ubase_size,
- (unsigned long) sizeof(*kinfo));
+ "Base size %zu, need %zu (version mismatch?)\n",
+ ubase_size, sz);
ret = -EINVAL;
goto bail;
}
@@ -95,7 +118,9 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk;
kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen /
pd->port_rcvegrbuf_chunks;
- kinfo->spi_tidcnt = dd->ipath_rcvtidcnt;
+ kinfo->spi_tidcnt = dd->ipath_rcvtidcnt / subport_cnt;
+ if (master)
+ kinfo->spi_tidcnt += dd->ipath_rcvtidcnt % subport_cnt;
/*
* for this use, may be ipath_cfgports summed over all chips that
* are are configured and present
@@ -118,31 +143,75 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
* page_address() macro worked, but in 2.6.11, even that returns the
* full 64 bit address (upper bits all 1's). So far, using the
* physical addresses (or chip offsets, for chip mapping) works, but
- * no doubt some future kernel release will chang that, and we'll be
- * on to yet another method of dealing with this
+ * no doubt some future kernel release will change that, and we'll be
+ * on to yet another method of dealing with this.
*/
kinfo->spi_rcvhdr_base = (u64) pd->port_rcvhdrq_phys;
- kinfo->spi_rcvhdr_tailaddr = (u64)pd->port_rcvhdrqtailaddr_phys;
+ kinfo->spi_rcvhdr_tailaddr = (u64) pd->port_rcvhdrqtailaddr_phys;
kinfo->spi_rcv_egrbufs = (u64) pd->port_rcvegr_phys;
kinfo->spi_pioavailaddr = (u64) dd->ipath_pioavailregs_phys;
kinfo->spi_status = (u64) kinfo->spi_pioavailaddr +
(void *) dd->ipath_statusp -
(void *) dd->ipath_pioavailregs_dma;
- kinfo->spi_piobufbase = (u64) pd->port_piobufs;
- kinfo->__spi_uregbase =
- dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+ if (!shared) {
+ kinfo->spi_piocnt = dd->ipath_pbufsport;
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs;
+ kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
+ dd->ipath_palign * pd->port_port;
+ } else if (master) {
+ kinfo->spi_piocnt = (dd->ipath_pbufsport / subport_cnt) +
+ (dd->ipath_pbufsport % subport_cnt);
+ /* Master's PIO buffers are after all the slave's */
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs +
+ dd->ipath_palign *
+ (dd->ipath_pbufsport - kinfo->spi_piocnt);
+ kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
+ dd->ipath_palign * pd->port_port;
+ } else {
+ unsigned slave = subport_fp(fp) - 1;
+
+ kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt;
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs +
+ dd->ipath_palign * kinfo->spi_piocnt * slave;
+ kinfo->__spi_uregbase = ((u64) pd->subport_uregbase +
+ PAGE_SIZE * slave) & MMAP64_MASK;
- kinfo->spi_pioindex = dd->ipath_pbufsport * (pd->port_port - 1);
- kinfo->spi_piocnt = dd->ipath_pbufsport;
+ kinfo->spi_rcvhdr_base = ((u64) pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * slave) & MMAP64_MASK;
+ kinfo->spi_rcvhdr_tailaddr =
+ (u64) pd->port_rcvhdrqtailaddr_phys & MMAP64_MASK;
+ kinfo->spi_rcv_egrbufs = ((u64) pd->subport_rcvegrbuf +
+ dd->ipath_rcvegrcnt * dd->ipath_rcvegrbufsize * slave) &
+ MMAP64_MASK;
+ }
+
+ kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) /
+ dd->ipath_palign;
kinfo->spi_pioalign = dd->ipath_palign;
kinfo->spi_qpair = IPATH_KD_QP;
kinfo->spi_piosize = dd->ipath_ibmaxlen;
kinfo->spi_mtu = dd->ipath_ibmaxlen; /* maxlen, not ibmtu */
kinfo->spi_port = pd->port_port;
+ kinfo->spi_subport = subport_fp(fp);
kinfo->spi_sw_version = IPATH_KERN_SWVERSION;
kinfo->spi_hw_version = dd->ipath_revision;
+ if (master) {
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_MASTER;
+ kinfo->spi_subport_uregbase =
+ (u64) pd->subport_uregbase & MMAP64_MASK;
+ kinfo->spi_subport_rcvegrbuf =
+ (u64) pd->subport_rcvegrbuf & MMAP64_MASK;
+ kinfo->spi_subport_rcvhdr_base =
+ (u64) pd->subport_rcvhdr_base & MMAP64_MASK;
+ ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n",
+ kinfo->spi_port, kinfo->spi_runtime_flags,
+ (unsigned long long) kinfo->spi_subport_uregbase,
+ (unsigned long long) kinfo->spi_subport_rcvegrbuf,
+ (unsigned long long) kinfo->spi_subport_rcvhdr_base);
+ }
+
if (copy_to_user(ubase, kinfo, sizeof(*kinfo)))
ret = -EFAULT;
@@ -154,6 +223,7 @@ bail:
/**
* ipath_tid_update - update a port TID
* @pd: the port
+ * @fp: the ipath device file
* @ti: the TID information
*
* The new implementation as of Oct 2004 is that the driver assigns
@@ -176,11 +246,11 @@ bail:
* virtually contiguous pages, that should change to improve
* performance.
*/
-static int ipath_tid_update(struct ipath_portdata *pd,
+static int ipath_tid_update(struct ipath_portdata *pd, struct file *fp,
const struct ipath_tid_info *ti)
{
int ret = 0, ntids;
- u32 tid, porttid, cnt, i, tidcnt;
+ u32 tid, porttid, cnt, i, tidcnt, tidoff;
u16 *tidlist;
struct ipath_devdata *dd = pd->port_dd;
u64 physaddr;
@@ -188,6 +258,7 @@ static int ipath_tid_update(struct ipath_portdata *pd,
u64 __iomem *tidbase;
unsigned long tidmap[8];
struct page **pagep = NULL;
+ unsigned subport = subport_fp(fp);
if (!dd->ipath_pageshadow) {
ret = -ENOMEM;
@@ -204,20 +275,34 @@ static int ipath_tid_update(struct ipath_portdata *pd,
ret = -EFAULT;
goto done;
}
- tidcnt = dd->ipath_rcvtidcnt;
- if (cnt >= tidcnt) {
+ porttid = pd->port_port * dd->ipath_rcvtidcnt;
+ if (!pd->port_subport_cnt) {
+ tidcnt = dd->ipath_rcvtidcnt;
+ tid = pd->port_tidcursor;
+ tidoff = 0;
+ } else if (!subport) {
+ tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) +
+ (dd->ipath_rcvtidcnt % pd->port_subport_cnt);
+ tidoff = dd->ipath_rcvtidcnt - tidcnt;
+ porttid += tidoff;
+ tid = tidcursor_fp(fp);
+ } else {
+ tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt;
+ tidoff = tidcnt * (subport - 1);
+ porttid += tidoff;
+ tid = tidcursor_fp(fp);
+ }
+ if (cnt > tidcnt) {
/* make sure it all fits in port_tid_pg_list */
dev_info(&dd->pcidev->dev, "Process tried to allocate %u "
"TIDs, only trying max (%u)\n", cnt, tidcnt);
cnt = tidcnt;
}
- pagep = (struct page **)pd->port_tid_pg_list;
- tidlist = (u16 *) (&pagep[cnt]);
+ pagep = &((struct page **) pd->port_tid_pg_list)[tidoff];
+ tidlist = &((u16 *) &pagep[dd->ipath_rcvtidcnt])[tidoff];
memset(tidmap, 0, sizeof(tidmap));
- tid = pd->port_tidcursor;
/* before decrement; chip actual # */
- porttid = pd->port_port * tidcnt;
ntids = tidcnt;
tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) +
dd->ipath_rcvtidbase +
@@ -274,16 +359,19 @@ static int ipath_tid_update(struct ipath_portdata *pd,
ret = -ENOMEM;
break;
}
- tidlist[i] = tid;
+ tidlist[i] = tid + tidoff;
ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, "
- "vaddr %lx\n", i, tid, vaddr);
+ "vaddr %lx\n", i, tid + tidoff, vaddr);
/* we "know" system pages and TID pages are same size */
dd->ipath_pageshadow[porttid + tid] = pagep[i];
+ dd->ipath_physshadow[porttid + tid] = ipath_map_page(
+ dd->pcidev, pagep[i], 0, PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
/*
* don't need atomic or it's overhead
*/
__set_bit(tid, tidmap);
- physaddr = page_to_phys(pagep[i]);
+ physaddr = dd->ipath_physshadow[porttid + tid];
ipath_stats.sps_pagelocks++;
ipath_cdbg(VERBOSE,
"TID %u, vaddr %lx, physaddr %llx pgp %p\n",
@@ -317,6 +405,9 @@ static int ipath_tid_update(struct ipath_portdata *pd,
tid);
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
dd->ipath_tidinvalid);
+ pci_unmap_page(dd->pcidev,
+ dd->ipath_physshadow[porttid + tid],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
dd->ipath_pageshadow[porttid + tid] = NULL;
ipath_stats.sps_pageunlocks++;
}
@@ -341,7 +432,10 @@ static int ipath_tid_update(struct ipath_portdata *pd,
}
if (tid == tidcnt)
tid = 0;
- pd->port_tidcursor = tid;
+ if (!pd->port_subport_cnt)
+ pd->port_tidcursor = tid;
+ else
+ tidcursor_fp(fp) = tid;
}
done:
@@ -354,6 +448,7 @@ done:
/**
* ipath_tid_free - free a port TID
* @pd: the port
+ * @subport: the subport
* @ti: the TID info
*
* right now we are unlocking one page at a time, but since
@@ -367,7 +462,7 @@ done:
* they pass in to us.
*/
-static int ipath_tid_free(struct ipath_portdata *pd,
+static int ipath_tid_free(struct ipath_portdata *pd, unsigned subport,
const struct ipath_tid_info *ti)
{
int ret = 0;
@@ -388,11 +483,20 @@ static int ipath_tid_free(struct ipath_portdata *pd,
}
porttid = pd->port_port * dd->ipath_rcvtidcnt;
+ if (!pd->port_subport_cnt)
+ tidcnt = dd->ipath_rcvtidcnt;
+ else if (!subport) {
+ tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) +
+ (dd->ipath_rcvtidcnt % pd->port_subport_cnt);
+ porttid += dd->ipath_rcvtidcnt - tidcnt;
+ } else {
+ tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt;
+ porttid += tidcnt * (subport - 1);
+ }
tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) +
dd->ipath_rcvtidbase +
porttid * sizeof(*tidbase));
- tidcnt = dd->ipath_rcvtidcnt;
limit = sizeof(tidmap) * BITS_PER_BYTE;
if (limit > tidcnt)
/* just in case size changes in future */
@@ -417,6 +521,9 @@ static int ipath_tid_free(struct ipath_portdata *pd,
pd->port_pid, tid);
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
dd->ipath_tidinvalid);
+ pci_unmap_page(dd->pcidev,
+ dd->ipath_physshadow[porttid + tid],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
ipath_release_user_pages(
&dd->ipath_pageshadow[porttid + tid], 1);
dd->ipath_pageshadow[porttid + tid] = NULL;
@@ -581,20 +688,24 @@ bail:
/**
* ipath_manage_rcvq - manage a port's receive queue
* @pd: the port
+ * @subport: the subport
* @start_stop: action to carry out
*
* start_stop == 0 disables receive on the port, for use in queue
* overflow conditions. start_stop==1 re-enables, to be used to
* re-init the software copy of the head register
*/
-static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
+static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport,
+ int start_stop)
{
struct ipath_devdata *dd = pd->port_dd;
u64 tval;
- ipath_cdbg(PROC, "%sabling rcv for unit %u port %u\n",
+ ipath_cdbg(PROC, "%sabling rcv for unit %u port %u:%u\n",
start_stop ? "en" : "dis", dd->ipath_unit,
- pd->port_port);
+ pd->port_port, subport);
+ if (subport)
+ goto bail;
/* atomically clear receive enable port. */
if (start_stop) {
/*
@@ -609,7 +720,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
* updated and correct itself, even in the face of software
* bugs.
*/
- *pd->port_rcvhdrtail_kvaddr = 0;
+ *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0;
set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
&dd->ipath_rcvctrl);
} else
@@ -630,6 +741,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
}
/* always; new head should be equal to new tail; see above */
+bail:
return 0;
}
@@ -687,6 +799,36 @@ static void ipath_clean_part_key(struct ipath_portdata *pd,
}
}
+/*
+ * Initialize the port data with the receive buffer sizes
+ * so this can be done while the master port is locked.
+ * Otherwise, there is a race with a slave opening the port
+ * and seeing these fields uninitialized.
+ */
+static void init_user_egr_sizes(struct ipath_portdata *pd)
+{
+ struct ipath_devdata *dd = pd->port_dd;
+ unsigned egrperchunk, egrcnt, size;
+
+ /*
+ * to avoid wasting a lot of memory, we allocate 32KB chunks of
+ * physically contiguous memory, advance through it until used up
+ * and then allocate more. Of course, we need memory to store those
+ * extra pointers, now. Started out with 256KB, but under heavy
+ * memory pressure (creating large files and then copying them over
+ * NFS while doing lots of MPI jobs), we hit some allocation
+ * failures, even though we can sleep... (2.6.10) Still get
+ * failures at 64K. 32K is the lowest we can go without wasting
+ * additional memory.
+ */
+ size = 0x8000;
+ egrperchunk = size / dd->ipath_rcvegrbufsize;
+ egrcnt = dd->ipath_rcvegrcnt;
+ pd->port_rcvegrbuf_chunks = (egrcnt + egrperchunk - 1) / egrperchunk;
+ pd->port_rcvegrbufs_perchunk = egrperchunk;
+ pd->port_rcvegrbuf_size = size;
+}
+
/**
* ipath_create_user_egr - allocate eager TID buffers
* @pd: the port to allocate TID buffers for
@@ -702,7 +844,7 @@ static void ipath_clean_part_key(struct ipath_portdata *pd,
static int ipath_create_user_egr(struct ipath_portdata *pd)
{
struct ipath_devdata *dd = pd->port_dd;
- unsigned e, egrcnt, alloced, egrperchunk, chunk, egrsize, egroff;
+ unsigned e, egrcnt, egrperchunk, chunk, egrsize, egroff;
size_t size;
int ret;
gfp_t gfp_flags;
@@ -722,31 +864,18 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid "
"offset %x, egrsize %u\n", egrcnt, egroff, egrsize);
- /*
- * to avoid wasting a lot of memory, we allocate 32KB chunks of
- * physically contiguous memory, advance through it until used up
- * and then allocate more. Of course, we need memory to store those
- * extra pointers, now. Started out with 256KB, but under heavy
- * memory pressure (creating large files and then copying them over
- * NFS while doing lots of MPI jobs), we hit some allocation
- * failures, even though we can sleep... (2.6.10) Still get
- * failures at 64K. 32K is the lowest we can go without wasting
- * additional memory.
- */
- size = 0x8000;
- alloced = ALIGN(egrsize * egrcnt, size);
- egrperchunk = size / egrsize;
- chunk = (egrcnt + egrperchunk - 1) / egrperchunk;
- pd->port_rcvegrbuf_chunks = chunk;
- pd->port_rcvegrbufs_perchunk = egrperchunk;
- pd->port_rcvegrbuf_size = size;
- pd->port_rcvegrbuf = vmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]));
+ chunk = pd->port_rcvegrbuf_chunks;
+ egrperchunk = pd->port_rcvegrbufs_perchunk;
+ size = pd->port_rcvegrbuf_size;
+ pd->port_rcvegrbuf = kmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]),
+ GFP_KERNEL);
if (!pd->port_rcvegrbuf) {
ret = -ENOMEM;
goto bail;
}
pd->port_rcvegrbuf_phys =
- vmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]));
+ kmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]),
+ GFP_KERNEL);
if (!pd->port_rcvegrbuf_phys) {
ret = -ENOMEM;
goto bail_rcvegrbuf;
@@ -791,105 +920,23 @@ bail_rcvegrbuf_phys:
pd->port_rcvegrbuf_phys[e]);
}
- vfree(pd->port_rcvegrbuf_phys);
+ kfree(pd->port_rcvegrbuf_phys);
pd->port_rcvegrbuf_phys = NULL;
bail_rcvegrbuf:
- vfree(pd->port_rcvegrbuf);
+ kfree(pd->port_rcvegrbuf);
pd->port_rcvegrbuf = NULL;
bail:
return ret;
}
-static int ipath_do_user_init(struct ipath_portdata *pd,
- const struct ipath_user_info *uinfo)
-{
- int ret = 0;
- struct ipath_devdata *dd = pd->port_dd;
- u32 head32;
-
- /* for now, if major version is different, bail */
- if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
- dev_info(&dd->pcidev->dev,
- "User major version %d not same as driver "
- "major %d\n", uinfo->spu_userversion >> 16,
- IPATH_USER_SWMAJOR);
- ret = -ENODEV;
- goto done;
- }
-
- if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
- ipath_dbg("User minor version %d not same as driver "
- "minor %d\n", uinfo->spu_userversion & 0xffff,
- IPATH_USER_SWMINOR);
-
- if (uinfo->spu_rcvhdrsize) {
- ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize);
- if (ret)
- goto done;
- }
-
- /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
-
- /* for right now, kernel piobufs are at end, so port 1 is at 0 */
- pd->port_piobufs = dd->ipath_piobufbase +
- dd->ipath_pbufsport * (pd->port_port -
- 1) * dd->ipath_palign;
- ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n",
- pd->port_port, pd->port_piobufs);
-
- /*
- * Now allocate the rcvhdr Q and eager TIDs; skip the TID
- * array for time being. If pd->port_port > chip-supported,
- * we need to do extra stuff here to handle by handling overflow
- * through port 0, someday
- */
- ret = ipath_create_rcvhdrq(dd, pd);
- if (!ret)
- ret = ipath_create_user_egr(pd);
- if (ret)
- goto done;
-
- /*
- * set the eager head register for this port to the current values
- * of the tail pointers, since we don't know if they were
- * updated on last use of the port.
- */
- head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
- ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
- dd->ipath_lastegrheads[pd->port_port] = -1;
- dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
- ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
- pd->port_port, head32);
- pd->port_tidcursor = 0; /* start at beginning after open */
- /*
- * now enable the port; the tail registers will be written to memory
- * by the chip as soon as it sees the write to
- * dd->ipath_kregs->kr_rcvctrl. The update only happens on
- * transition from 0 to 1, so clear it first, then set it as part of
- * enabling the port. This will (very briefly) affect any other
- * open ports, but it shouldn't be long enough to be an issue.
- * We explictly set the in-memory copy to 0 beforehand, so we don't
- * have to wait to be sure the DMA update has happened.
- */
- *pd->port_rcvhdrtail_kvaddr = 0ULL;
- set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
- &dd->ipath_rcvctrl);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
- dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
- dd->ipath_rcvctrl);
-done:
- return ret;
-}
-
/* common code for the mappings on dma_alloc_coherent mem */
static int ipath_mmap_mem(struct vm_area_struct *vma,
- struct ipath_portdata *pd, unsigned len,
- int write_ok, dma_addr_t addr, char *what)
+ struct ipath_portdata *pd, unsigned len, int write_ok,
+ void *kvaddr, char *what)
{
struct ipath_devdata *dd = pd->port_dd;
- unsigned pfn = (unsigned long)addr >> PAGE_SHIFT;
+ unsigned long pfn;
int ret;
if ((vma->vm_end - vma->vm_start) > len) {
@@ -912,17 +959,17 @@ static int ipath_mmap_mem(struct vm_area_struct *vma,
vma->vm_flags &= ~VM_MAYWRITE;
}
+ pfn = virt_to_phys(kvaddr) >> PAGE_SHIFT;
ret = remap_pfn_range(vma, vma->vm_start, pfn,
len, vma->vm_page_prot);
if (ret)
- dev_info(&dd->pcidev->dev,
- "%s port%u mmap of %lx, %x bytes r%c failed: %d\n",
- what, pd->port_port, (unsigned long)addr, len,
- write_ok?'w':'o', ret);
+ dev_info(&dd->pcidev->dev, "%s port%u mmap of %lx, %x "
+ "bytes r%c failed: %d\n", what, pd->port_port,
+ pfn, len, write_ok?'w':'o', ret);
else
- ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes r%c\n",
- what, pd->port_port, (unsigned long)addr, len,
- write_ok?'w':'o');
+ ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes "
+ "r%c\n", what, pd->port_port, pfn, len,
+ write_ok?'w':'o');
bail:
return ret;
}
@@ -957,7 +1004,8 @@ static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd,
static int mmap_piobufs(struct vm_area_struct *vma,
struct ipath_devdata *dd,
- struct ipath_portdata *pd)
+ struct ipath_portdata *pd,
+ unsigned piobufs, unsigned piocnt)
{
unsigned long phys;
int ret;
@@ -968,16 +1016,15 @@ static int mmap_piobufs(struct vm_area_struct *vma,
* process data, and catches users who might try to read the i/o
* space due to a bug.
*/
- if ((vma->vm_end - vma->vm_start) >
- (dd->ipath_pbufsport * dd->ipath_palign)) {
+ if ((vma->vm_end - vma->vm_start) > (piocnt * dd->ipath_palign)) {
dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: "
"reqlen %lx > PAGE\n",
vma->vm_end - vma->vm_start);
- ret = -EFAULT;
+ ret = -EINVAL;
goto bail;
}
- phys = dd->ipath_physaddr + pd->port_piobufs;
+ phys = dd->ipath_physaddr + piobufs;
/*
* Don't mark this as non-cached, or we don't get the
@@ -1011,7 +1058,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
struct ipath_devdata *dd = pd->port_dd;
unsigned long start, size;
size_t total_size, i;
- dma_addr_t *phys;
+ unsigned long pfn;
int ret;
size = pd->port_rcvegrbuf_size;
@@ -1021,7 +1068,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
"reqlen %lx > actual %lx\n",
vma->vm_end - vma->vm_start,
(unsigned long) total_size);
- ret = -EFAULT;
+ ret = -EINVAL;
goto bail;
}
@@ -1035,11 +1082,11 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
vma->vm_flags &= ~VM_MAYWRITE;
start = vma->vm_start;
- phys = pd->port_rcvegrbuf_phys;
for (i = 0; i < pd->port_rcvegrbuf_chunks; i++, start += size) {
- ret = remap_pfn_range(vma, start, phys[i] >> PAGE_SHIFT,
- size, vma->vm_page_prot);
+ pfn = virt_to_phys(pd->port_rcvegrbuf[i]) >> PAGE_SHIFT;
+ ret = remap_pfn_range(vma, start, pfn, size,
+ vma->vm_page_prot);
if (ret < 0)
goto bail;
}
@@ -1049,6 +1096,122 @@ bail:
return ret;
}
+/*
+ * ipath_file_vma_nopage - handle a VMA page fault.
+ */
+static struct page *ipath_file_vma_nopage(struct vm_area_struct *vma,
+ unsigned long address, int *type)
+{
+ unsigned long offset = address - vma->vm_start;
+ struct page *page = NOPAGE_SIGBUS;
+ void *pageptr;
+
+ /*
+ * Convert the vmalloc address into a struct page.
+ */
+ pageptr = (void *)(offset + (vma->vm_pgoff << PAGE_SHIFT));
+ page = vmalloc_to_page(pageptr);
+ if (!page)
+ goto out;
+
+ /* Increment the reference count. */
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+out:
+ return page;
+}
+
+static struct vm_operations_struct ipath_file_vm_ops = {
+ .nopage = ipath_file_vma_nopage,
+};
+
+static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr,
+ struct ipath_portdata *pd, unsigned subport)
+{
+ unsigned long len;
+ struct ipath_devdata *dd;
+ void *addr;
+ size_t size;
+ int ret;
+
+ /* If the port is not shared, all addresses should be physical */
+ if (!pd->port_subport_cnt) {
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ dd = pd->port_dd;
+ size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size;
+
+ /*
+ * Master has all the slave uregbase, rcvhdrq, and
+ * rcvegrbufs mmapped.
+ */
+ if (subport == 0) {
+ unsigned num_slaves = pd->port_subport_cnt - 1;
+
+ if (pgaddr == ((u64) pd->subport_uregbase & MMAP64_MASK)) {
+ addr = pd->subport_uregbase;
+ size = PAGE_SIZE * num_slaves;
+ } else if (pgaddr == ((u64) pd->subport_rcvhdr_base &
+ MMAP64_MASK)) {
+ addr = pd->subport_rcvhdr_base;
+ size = pd->port_rcvhdrq_size * num_slaves;
+ } else if (pgaddr == ((u64) pd->subport_rcvegrbuf &
+ MMAP64_MASK)) {
+ addr = pd->subport_rcvegrbuf;
+ size *= num_slaves;
+ } else {
+ ret = -EINVAL;
+ goto bail;
+ }
+ } else if (pgaddr == (((u64) pd->subport_uregbase +
+ PAGE_SIZE * (subport - 1)) & MMAP64_MASK)) {
+ addr = pd->subport_uregbase + PAGE_SIZE * (subport - 1);
+ size = PAGE_SIZE;
+ } else if (pgaddr == (((u64) pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * (subport - 1)) &
+ MMAP64_MASK)) {
+ addr = pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * (subport - 1);
+ size = pd->port_rcvhdrq_size;
+ } else if (pgaddr == (((u64) pd->subport_rcvegrbuf +
+ size * (subport - 1)) & MMAP64_MASK)) {
+ addr = pd->subport_rcvegrbuf + size * (subport - 1);
+ /* rcvegrbufs are read-only on the slave */
+ if (vma->vm_flags & VM_WRITE) {
+ dev_info(&dd->pcidev->dev,
+ "Can't map eager buffers as "
+ "writable (flags=%lx)\n", vma->vm_flags);
+ ret = -EPERM;
+ goto bail;
+ }
+ /*
+ * Don't allow permission to later change to writeable
+ * with mprotect.
+ */
+ vma->vm_flags &= ~VM_MAYWRITE;
+ } else {
+ ret = -EINVAL;
+ goto bail;
+ }
+ len = vma->vm_end - vma->vm_start;
+ if (len > size) {
+ ipath_cdbg(MM, "FAIL: reqlen %lx > %zx\n", len, size);
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT;
+ vma->vm_ops = &ipath_file_vm_ops;
+ vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+ ret = 0;
+
+bail:
+ return ret;
+}
+
/**
* ipath_mmap - mmap various structures into user space
* @fp: the file pointer
@@ -1064,73 +1227,99 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
struct ipath_portdata *pd;
struct ipath_devdata *dd;
u64 pgaddr, ureg;
+ unsigned piobufs, piocnt;
int ret;
pd = port_fp(fp);
+ if (!pd) {
+ ret = -EINVAL;
+ goto bail;
+ }
dd = pd->port_dd;
/*
* This is the ipath_do_user_init() code, mapping the shared buffers
* into the user process. The address referred to by vm_pgoff is the
- * virtual, not physical, address; we only do one mmap for each
- * space mapped.
+ * file offset passed via mmap(). For shared ports, this is the
+ * kernel vmalloc() address of the pages to share with the master.
+ * For non-shared or master ports, this is a physical address.
+ * We only do one mmap for each space mapped.
*/
pgaddr = vma->vm_pgoff << PAGE_SHIFT;
/*
- * Must fit in 40 bits for our hardware; some checked elsewhere,
- * but we'll be paranoid. Check for 0 is mostly in case one of the
- * allocations failed, but user called mmap anyway. We want to catch
- * that before it can match.
+ * Check for 0 in case one of the allocations failed, but user
+ * called mmap anyway.
*/
- if (!pgaddr || pgaddr >= (1ULL<<40)) {
- ipath_dev_err(dd, "Bad phys addr %llx, start %lx, end %lx\n",
- (unsigned long long)pgaddr, vma->vm_start, vma->vm_end);
- return -EINVAL;
+ if (!pgaddr) {
+ ret = -EINVAL;
+ goto bail;
}
- /* just the offset of the port user registers, not physical addr */
- ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
-
- ipath_cdbg(MM, "ushare: pgaddr %llx vm_start=%lx, vmlen %lx\n",
+ ipath_cdbg(MM, "pgaddr %llx vm_start=%lx len %lx port %u:%u:%u\n",
(unsigned long long) pgaddr, vma->vm_start,
- vma->vm_end - vma->vm_start);
+ vma->vm_end - vma->vm_start, dd->ipath_unit,
+ pd->port_port, subport_fp(fp));
- if (vma->vm_start & (PAGE_SIZE-1)) {
- ipath_dev_err(dd,
- "vm_start not aligned: %lx, end=%lx phys %lx\n",
- vma->vm_start, vma->vm_end, (unsigned long)pgaddr);
- ret = -EINVAL;
+ /*
+ * Physical addresses must fit in 40 bits for our hardware.
+ * Check for kernel virtual addresses first, anything else must
+ * match a HW or memory address.
+ */
+ if (pgaddr >= (1ULL<<40)) {
+ ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp));
+ goto bail;
}
- else if (pgaddr == ureg)
+
+ if (!pd->port_subport_cnt) {
+ /* port is not shared */
+ ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+ piocnt = dd->ipath_pbufsport;
+ piobufs = pd->port_piobufs;
+ } else if (!subport_fp(fp)) {
+ /* caller is the master */
+ ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+ piocnt = (dd->ipath_pbufsport / pd->port_subport_cnt) +
+ (dd->ipath_pbufsport % pd->port_subport_cnt);
+ piobufs = pd->port_piobufs +
+ dd->ipath_palign * (dd->ipath_pbufsport - piocnt);
+ } else {
+ unsigned slave = subport_fp(fp) - 1;
+
+ /* caller is a slave */
+ ureg = 0;
+ piocnt = dd->ipath_pbufsport / pd->port_subport_cnt;
+ piobufs = pd->port_piobufs + dd->ipath_palign * piocnt * slave;
+ }
+
+ if (pgaddr == ureg)
ret = mmap_ureg(vma, dd, ureg);
- else if (pgaddr == pd->port_piobufs)
- ret = mmap_piobufs(vma, dd, pd);
- else if (pgaddr == (u64) pd->port_rcvegr_phys)
+ else if (pgaddr == piobufs)
+ ret = mmap_piobufs(vma, dd, pd, piobufs, piocnt);
+ else if (pgaddr == dd->ipath_pioavailregs_phys)
+ /* in-memory copy of pioavail registers */
+ ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
+ (void *) dd->ipath_pioavailregs_dma,
+ "pioavail registers");
+ else if (subport_fp(fp))
+ /* Subports don't mmap the physical receive buffers */
+ ret = -EINVAL;
+ else if (pgaddr == pd->port_rcvegr_phys)
ret = mmap_rcvegrbufs(vma, pd);
- else if (pgaddr == (u64) pd->port_rcvhdrq_phys) {
+ else if (pgaddr == (u64) pd->port_rcvhdrq_phys)
/*
* The rcvhdrq itself; readonly except on HT (so have
* to allow writable mapping), multiple pages, contiguous
* from an i/o perspective.
*/
- unsigned total_size =
- ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize
- * sizeof(u32), PAGE_SIZE);
- ret = ipath_mmap_mem(vma, pd, total_size, 1,
- pd->port_rcvhdrq_phys,
+ ret = ipath_mmap_mem(vma, pd, pd->port_rcvhdrq_size, 1,
+ pd->port_rcvhdrq,
"rcvhdrq");
- }
- else if (pgaddr == (u64)pd->port_rcvhdrqtailaddr_phys)
+ else if (pgaddr == (u64) pd->port_rcvhdrqtailaddr_phys)
/* in-memory copy of rcvhdrq tail register */
ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
- pd->port_rcvhdrqtailaddr_phys,
+ pd->port_rcvhdrtail_kvaddr,
"rcvhdrq tail");
- else if (pgaddr == dd->ipath_pioavailregs_phys)
- /* in-memory copy of pioavail registers */
- ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
- dd->ipath_pioavailregs_phys,
- "pioavail registers");
else
ret = -EINVAL;
@@ -1138,9 +1327,10 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
if (ret < 0)
dev_info(&dd->pcidev->dev,
- "Failure %d on addr %lx, off %lx\n",
- -ret, vma->vm_start, vma->vm_pgoff);
-
+ "Failure %d on off %llx len %lx\n",
+ -ret, (unsigned long long)pgaddr,
+ vma->vm_end - vma->vm_start);
+bail:
return ret;
}
@@ -1154,6 +1344,8 @@ static unsigned int ipath_poll(struct file *fp,
struct ipath_devdata *dd;
pd = port_fp(fp);
+ if (!pd)
+ goto bail;
dd = pd->port_dd;
bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT;
@@ -1176,7 +1368,7 @@ static unsigned int ipath_poll(struct file *fp,
if (tail == head) {
set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
- if(dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */
+ if (dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */
(void)ipath_write_ureg(dd, ur_rcvhdrhead,
dd->ipath_rhdrhead_intr_off
| head, pd->port_port);
@@ -1200,18 +1392,80 @@ static unsigned int ipath_poll(struct file *fp,
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
+bail:
return pollflag;
}
+static int init_subports(struct ipath_devdata *dd,
+ struct ipath_portdata *pd,
+ const struct ipath_user_info *uinfo)
+{
+ int ret = 0;
+ unsigned num_slaves;
+ size_t size;
+
+ /* Old user binaries don't know about subports */
+ if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
+ goto bail;
+ /*
+ * If the user is requesting zero or one port,
+ * skip the subport allocation.
+ */
+ if (uinfo->spu_subport_cnt <= 1)
+ goto bail;
+ if (uinfo->spu_subport_cnt > 4) {
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ num_slaves = uinfo->spu_subport_cnt - 1;
+ pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves);
+ if (!pd->subport_uregbase) {
+ ret = -ENOMEM;
+ goto bail;
+ }
+ /* Note: pd->port_rcvhdrq_size isn't initialized yet. */
+ size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
+ sizeof(u32), PAGE_SIZE) * num_slaves;
+ pd->subport_rcvhdr_base = vmalloc(size);
+ if (!pd->subport_rcvhdr_base) {
+ ret = -ENOMEM;
+ goto bail_ureg;
+ }
+
+ pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks *
+ pd->port_rcvegrbuf_size *
+ num_slaves);
+ if (!pd->subport_rcvegrbuf) {
+ ret = -ENOMEM;
+ goto bail_rhdr;
+ }
+
+ pd->port_subport_cnt = uinfo->spu_subport_cnt;
+ pd->port_subport_id = uinfo->spu_subport_id;
+ pd->active_slaves = 1;
+ goto bail;
+
+bail_rhdr:
+ vfree(pd->subport_rcvhdr_base);
+bail_ureg:
+ vfree(pd->subport_uregbase);
+ pd->subport_uregbase = NULL;
+bail:
+ return ret;
+}
+
static int try_alloc_port(struct ipath_devdata *dd, int port,
- struct file *fp)
+ struct file *fp,
+ const struct ipath_user_info *uinfo)
{
+ struct ipath_portdata *pd;
int ret;
- if (!dd->ipath_pd[port]) {
- void *p, *ptmp;
+ if (!(pd = dd->ipath_pd[port])) {
+ void *ptmp;
- p = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);
+ pd = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);
/*
* Allocate memory for use in ipath_tid_update() just once
@@ -1221,34 +1475,36 @@ static int try_alloc_port(struct ipath_devdata *dd, int port,
ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) +
dd->ipath_rcvtidcnt * sizeof(struct page **),
GFP_KERNEL);
- if (!p || !ptmp) {
+ if (!pd || !ptmp) {
ipath_dev_err(dd, "Unable to allocate portdata "
"memory, failing open\n");
ret = -ENOMEM;
- kfree(p);
+ kfree(pd);
kfree(ptmp);
goto bail;
}
- dd->ipath_pd[port] = p;
+ dd->ipath_pd[port] = pd;
dd->ipath_pd[port]->port_port = port;
dd->ipath_pd[port]->port_dd = dd;
dd->ipath_pd[port]->port_tid_pg_list = ptmp;
init_waitqueue_head(&dd->ipath_pd[port]->port_wait);
}
- if (!dd->ipath_pd[port]->port_cnt) {
- dd->ipath_pd[port]->port_cnt = 1;
- fp->private_data = (void *) dd->ipath_pd[port];
+ if (!pd->port_cnt) {
+ pd->userversion = uinfo->spu_userversion;
+ init_user_egr_sizes(pd);
+ if ((ret = init_subports(dd, pd, uinfo)) != 0)
+ goto bail;
ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n",
current->comm, current->pid, dd->ipath_unit,
port);
- dd->ipath_pd[port]->port_pid = current->pid;
- strncpy(dd->ipath_pd[port]->port_comm, current->comm,
- sizeof(dd->ipath_pd[port]->port_comm));
+ pd->port_cnt = 1;
+ port_fp(fp) = pd;
+ pd->port_pid = current->pid;
+ strncpy(pd->port_comm, current->comm, sizeof(pd->port_comm));
ipath_stats.sps_ports++;
ret = 0;
- goto bail;
- }
- ret = -EBUSY;
+ } else
+ ret = -EBUSY;
bail:
return ret;
@@ -1264,7 +1520,8 @@ static inline int usable(struct ipath_devdata *dd)
| IPATH_LINKUNK));
}
-static int find_free_port(int unit, struct file *fp)
+static int find_free_port(int unit, struct file *fp,
+ const struct ipath_user_info *uinfo)
{
struct ipath_devdata *dd = ipath_lookup(unit);
int ret, i;
@@ -1279,8 +1536,8 @@ static int find_free_port(int unit, struct file *fp)
goto bail;
}
- for (i = 0; i < dd->ipath_cfgports; i++) {
- ret = try_alloc_port(dd, i, fp);
+ for (i = 1; i < dd->ipath_cfgports; i++) {
+ ret = try_alloc_port(dd, i, fp, uinfo);
if (ret != -EBUSY)
goto bail;
}
@@ -1290,13 +1547,14 @@ bail:
return ret;
}
-static int find_best_unit(struct file *fp)
+static int find_best_unit(struct file *fp,
+ const struct ipath_user_info *uinfo)
{
int ret = 0, i, prefunit = -1, devmax;
int maxofallports, npresent, nup;
int ndev;
- (void) ipath_count_units(&npresent, &nup, &maxofallports);
+ devmax = ipath_count_units(&npresent, &nup, &maxofallports);
/*
* This code is present to allow a knowledgeable person to
@@ -1343,8 +1601,6 @@ static int find_best_unit(struct file *fp)
if (prefunit != -1)
devmax = prefunit + 1;
- else
- devmax = ipath_count_units(NULL, NULL, NULL);
recheck:
for (i = 1; i < maxofallports; i++) {
for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax;
@@ -1359,7 +1615,7 @@ recheck:
* next.
*/
continue;
- ret = try_alloc_port(dd, i, fp);
+ ret = try_alloc_port(dd, i, fp, uinfo);
if (!ret)
goto done;
}
@@ -1395,22 +1651,183 @@ done:
return ret;
}
+static int find_shared_port(struct file *fp,
+ const struct ipath_user_info *uinfo)
+{
+ int devmax, ndev, i;
+ int ret = 0;
+
+ devmax = ipath_count_units(NULL, NULL, NULL);
+
+ for (ndev = 0; ndev < devmax; ndev++) {
+ struct ipath_devdata *dd = ipath_lookup(ndev);
+
+ if (!dd)
+ continue;
+ for (i = 1; i < dd->ipath_cfgports; i++) {
+ struct ipath_portdata *pd = dd->ipath_pd[i];
+
+ /* Skip ports which are not yet open */
+ if (!pd || !pd->port_cnt)
+ continue;
+ /* Skip port if it doesn't match the requested one */
+ if (pd->port_subport_id != uinfo->spu_subport_id)
+ continue;
+ /* Verify the sharing process matches the master */
+ if (pd->port_subport_cnt != uinfo->spu_subport_cnt ||
+ pd->userversion != uinfo->spu_userversion ||
+ pd->port_cnt >= pd->port_subport_cnt) {
+ ret = -EINVAL;
+ goto done;
+ }
+ port_fp(fp) = pd;
+ subport_fp(fp) = pd->port_cnt++;
+ tidcursor_fp(fp) = 0;
+ pd->active_slaves |= 1 << subport_fp(fp);
+ ipath_cdbg(PROC,
+ "%s[%u] %u sharing %s[%u] unit:port %u:%u\n",
+ current->comm, current->pid,
+ subport_fp(fp),
+ pd->port_comm, pd->port_pid,
+ dd->ipath_unit, pd->port_port);
+ ret = 1;
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
static int ipath_open(struct inode *in, struct file *fp)
{
- int ret, user_minor;
+ /* The real work is performed later in ipath_assign_port() */
+ fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL);
+ return fp->private_data ? 0 : -ENOMEM;
+}
+
+
+/* Get port early, so can set affinity prior to memory allocation */
+static int ipath_assign_port(struct file *fp,
+ const struct ipath_user_info *uinfo)
+{
+ int ret;
+ int i_minor;
+ unsigned swminor;
+
+ /* Check to be sure we haven't already initialized this file */
+ if (port_fp(fp)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* for now, if major version is different, bail */
+ if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
+ ipath_dbg("User major version %d not same as driver "
+ "major %d\n", uinfo->spu_userversion >> 16,
+ IPATH_USER_SWMAJOR);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ swminor = uinfo->spu_userversion & 0xffff;
+ if (swminor != IPATH_USER_SWMINOR)
+ ipath_dbg("User minor version %d not same as driver "
+ "minor %d\n", swminor, IPATH_USER_SWMINOR);
mutex_lock(&ipath_mutex);
- user_minor = iminor(in) - IPATH_USER_MINOR_BASE;
+ if (swminor == IPATH_USER_SWMINOR && uinfo->spu_subport_cnt &&
+ (ret = find_shared_port(fp, uinfo))) {
+ mutex_unlock(&ipath_mutex);
+ if (ret > 0)
+ ret = 0;
+ goto done;
+ }
+
+ i_minor = iminor(fp->f_dentry->d_inode) - IPATH_USER_MINOR_BASE;
ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n",
- (long)in->i_rdev, user_minor);
+ (long)fp->f_dentry->d_inode->i_rdev, i_minor);
- if (user_minor)
- ret = find_free_port(user_minor - 1, fp);
+ if (i_minor)
+ ret = find_free_port(i_minor - 1, fp, uinfo);
else
- ret = find_best_unit(fp);
+ ret = find_best_unit(fp, uinfo);
mutex_unlock(&ipath_mutex);
+
+done:
+ return ret;
+}
+
+
+static int ipath_do_user_init(struct file *fp,
+ const struct ipath_user_info *uinfo)
+{
+ int ret;
+ struct ipath_portdata *pd;
+ struct ipath_devdata *dd;
+ u32 head32;
+
+ pd = port_fp(fp);
+ dd = pd->port_dd;
+
+ if (uinfo->spu_rcvhdrsize) {
+ ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize);
+ if (ret)
+ goto done;
+ }
+
+ /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
+
+ /* for right now, kernel piobufs are at end, so port 1 is at 0 */
+ pd->port_piobufs = dd->ipath_piobufbase +
+ dd->ipath_pbufsport * (pd->port_port - 1) * dd->ipath_palign;
+ ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n",
+ pd->port_port, pd->port_piobufs);
+
+ /*
+ * Now allocate the rcvhdr Q and eager TIDs; skip the TID
+ * array for time being. If pd->port_port > chip-supported,
+ * we need to do extra stuff here to handle by handling overflow
+ * through port 0, someday
+ */
+ ret = ipath_create_rcvhdrq(dd, pd);
+ if (!ret)
+ ret = ipath_create_user_egr(pd);
+ if (ret)
+ goto done;
+
+ /*
+ * set the eager head register for this port to the current values
+ * of the tail pointers, since we don't know if they were
+ * updated on last use of the port.
+ */
+ head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
+ ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
+ dd->ipath_lastegrheads[pd->port_port] = -1;
+ dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
+ ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
+ pd->port_port, head32);
+ pd->port_tidcursor = 0; /* start at beginning after open */
+ /*
+ * now enable the port; the tail registers will be written to memory
+ * by the chip as soon as it sees the write to
+ * dd->ipath_kregs->kr_rcvctrl. The update only happens on
+ * transition from 0 to 1, so clear it first, then set it as part of
+ * enabling the port. This will (very briefly) affect any other
+ * open ports, but it shouldn't be long enough to be an issue.
+ * We explictly set the in-memory copy to 0 beforehand, so we don't
+ * have to wait to be sure the DMA update has happened.
+ */
+ *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0ULL;
+ set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+ &dd->ipath_rcvctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+ dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+ dd->ipath_rcvctrl);
+done:
return ret;
}
@@ -1433,6 +1850,8 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
if (!dd->ipath_pageshadow[i])
continue;
+ pci_unmap_page(dd->pcidev, dd->ipath_physshadow[i],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
ipath_release_user_pages_on_close(&dd->ipath_pageshadow[i],
1);
dd->ipath_pageshadow[i] = NULL;
@@ -1453,6 +1872,7 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
static int ipath_close(struct inode *in, struct file *fp)
{
int ret = 0;
+ struct ipath_filedata *fd;
struct ipath_portdata *pd;
struct ipath_devdata *dd;
unsigned port;
@@ -1462,9 +1882,24 @@ static int ipath_close(struct inode *in, struct file *fp)
mutex_lock(&ipath_mutex);
- pd = port_fp(fp);
- port = pd->port_port;
+ fd = (struct ipath_filedata *) fp->private_data;
fp->private_data = NULL;
+ pd = fd->pd;
+ if (!pd) {
+ mutex_unlock(&ipath_mutex);
+ goto bail;
+ }
+ if (--pd->port_cnt) {
+ /*
+ * XXX If the master closes the port before the slave(s),
+ * revoke the mmap for the eager receive queue so
+ * the slave(s) don't wait for receive data forever.
+ */
+ pd->active_slaves &= ~(1 << fd->subport);
+ mutex_unlock(&ipath_mutex);
+ goto bail;
+ }
+ port = pd->port_port;
dd = pd->port_dd;
if (pd->port_hdrqfull) {
@@ -1503,8 +1938,6 @@ static int ipath_close(struct inode *in, struct file *fp)
/* clean up the pkeys for this port user */
ipath_clean_part_key(pd, dd);
-
-
/*
* be paranoid, and never write 0's to these, just use an
* unused part of the port 0 tail page. Of course,
@@ -1523,39 +1956,49 @@ static int ipath_close(struct inode *in, struct file *fp)
i = dd->ipath_pbufsport * (port - 1);
ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
+ dd->ipath_f_clear_tids(dd, pd->port_port);
+
if (dd->ipath_pageshadow)
unlock_expected_tids(pd);
ipath_stats.sps_ports--;
ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
pd->port_comm, pd->port_pid,
dd->ipath_unit, port);
-
- dd->ipath_f_clear_tids(dd, pd->port_port);
}
- pd->port_cnt = 0;
pd->port_pid = 0;
-
dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */
mutex_unlock(&ipath_mutex);
ipath_free_pddata(dd, pd); /* after releasing the mutex */
+bail:
+ kfree(fd);
return ret;
}
-static int ipath_port_info(struct ipath_portdata *pd,
+static int ipath_port_info(struct ipath_portdata *pd, u16 subport,
struct ipath_port_info __user *uinfo)
{
struct ipath_port_info info;
int nup;
int ret;
+ size_t sz;
(void) ipath_count_units(NULL, &nup, NULL);
info.num_active = nup;
info.unit = pd->port_dd->ipath_unit;
info.port = pd->port_port;
+ info.subport = subport;
+ /* Don't return new fields if old library opened the port. */
+ if ((pd->userversion & 0xffff) == IPATH_USER_SWMINOR) {
+ /* Number of user ports available for this device. */
+ info.num_ports = pd->port_dd->ipath_cfgports - 1;
+ info.num_subports = pd->port_subport_cnt;
+ sz = sizeof(info);
+ } else
+ sz = sizeof(info) - 2 * sizeof(u16);
- if (copy_to_user(uinfo, &info, sizeof(info))) {
+ if (copy_to_user(uinfo, &info, sz)) {
ret = -EFAULT;
goto bail;
}
@@ -1565,6 +2008,16 @@ bail:
return ret;
}
+static int ipath_get_slave_info(struct ipath_portdata *pd,
+ void __user *slave_mask_addr)
+{
+ int ret = 0;
+
+ if (copy_to_user(slave_mask_addr, &pd->active_slaves, sizeof(u32)))
+ ret = -EFAULT;
+ return ret;
+}
+
static ssize_t ipath_write(struct file *fp, const char __user *data,
size_t count, loff_t *off)
{
@@ -1591,6 +2044,8 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
consumed = sizeof(cmd.type);
switch (cmd.type) {
+ case IPATH_CMD_ASSIGN_PORT:
+ case __IPATH_CMD_USER_INIT:
case IPATH_CMD_USER_INIT:
copy = sizeof(cmd.cmd.user_info);
dest = &cmd.cmd.user_info;
@@ -1617,6 +2072,11 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
dest = &cmd.cmd.part_key;
src = &ucmd->cmd.part_key;
break;
+ case IPATH_CMD_SLAVE_INFO:
+ copy = sizeof(cmd.cmd.slave_mask_addr);
+ dest = &cmd.cmd.slave_mask_addr;
+ src = &ucmd->cmd.slave_mask_addr;
+ break;
default:
ret = -EINVAL;
goto bail;
@@ -1634,34 +2094,55 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
consumed += copy;
pd = port_fp(fp);
+ if (!pd && cmd.type != __IPATH_CMD_USER_INIT &&
+ cmd.type != IPATH_CMD_ASSIGN_PORT) {
+ ret = -EINVAL;
+ goto bail;
+ }
switch (cmd.type) {
+ case IPATH_CMD_ASSIGN_PORT:
+ ret = ipath_assign_port(fp, &cmd.cmd.user_info);
+ if (ret)
+ goto bail;
+ break;
+ case __IPATH_CMD_USER_INIT:
+ /* backwards compatibility, get port first */
+ ret = ipath_assign_port(fp, &cmd.cmd.user_info);
+ if (ret)
+ goto bail;
+ /* and fall through to current version. */
case IPATH_CMD_USER_INIT:
- ret = ipath_do_user_init(pd, &cmd.cmd.user_info);
- if (ret < 0)
+ ret = ipath_do_user_init(fp, &cmd.cmd.user_info);
+ if (ret)
goto bail;
ret = ipath_get_base_info(
- pd, (void __user *) (unsigned long)
+ fp, (void __user *) (unsigned long)
cmd.cmd.user_info.spu_base_info,
cmd.cmd.user_info.spu_base_info_size);
break;
case IPATH_CMD_RECV_CTRL:
- ret = ipath_manage_rcvq(pd, cmd.cmd.recv_ctrl);
+ ret = ipath_manage_rcvq(pd, subport_fp(fp), cmd.cmd.recv_ctrl);
break;
case IPATH_CMD_PORT_INFO:
- ret = ipath_port_info(pd,
+ ret = ipath_port_info(pd, subport_fp(fp),
(struct ipath_port_info __user *)
(unsigned long) cmd.cmd.port_info);
break;
case IPATH_CMD_TID_UPDATE:
- ret = ipath_tid_update(pd, &cmd.cmd.tid_info);
+ ret = ipath_tid_update(pd, fp, &cmd.cmd.tid_info);
break;
case IPATH_CMD_TID_FREE:
- ret = ipath_tid_free(pd, &cmd.cmd.tid_info);
+ ret = ipath_tid_free(pd, subport_fp(fp), &cmd.cmd.tid_info);
break;
case IPATH_CMD_SET_PART_KEY:
ret = ipath_set_part_key(pd, cmd.cmd.part_key);
break;
+ case IPATH_CMD_SLAVE_INFO:
+ ret = ipath_get_slave_info(pd,
+ (void __user *) (unsigned long)
+ cmd.cmd.slave_mask_addr);
+ break;
}
if (ret >= 0)
@@ -1858,4 +2339,3 @@ void ipath_user_remove(struct ipath_devdata *dd)
bail:
return;
}
-
diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c
index a5eb30a06a5..a507d0b5be6 100644
--- a/drivers/infiniband/hw/ipath/ipath_fs.c
+++ b/drivers/infiniband/hw/ipath/ipath_fs.c
@@ -61,10 +61,9 @@ static int ipathfs_mknod(struct inode *dir, struct dentry *dentry,
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->u.generic_ip = data;
+ inode->i_private = data;
if ((mode & S_IFMT) == S_IFDIR) {
inode->i_op = &simple_dir_inode_operations;
inode->i_nlink++;
@@ -119,7 +118,7 @@ static ssize_t atomic_counters_read(struct file *file, char __user *buf,
u16 i;
struct ipath_devdata *dd;
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
for (i = 0; i < NUM_COUNTERS; i++)
counters[i] = ipath_snap_cntr(dd, i);
@@ -139,7 +138,7 @@ static ssize_t atomic_node_info_read(struct file *file, char __user *buf,
struct ipath_devdata *dd;
u64 guid;
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
guid = be64_to_cpu(dd->ipath_guid);
@@ -178,7 +177,7 @@ static ssize_t atomic_port_info_read(struct file *file, char __user *buf,
u32 tmp, tmp2;
struct ipath_devdata *dd;
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
/* so we only initialize non-zero fields. */
memset(portinfo, 0, sizeof portinfo);
@@ -325,7 +324,7 @@ static ssize_t flash_read(struct file *file, char __user *buf,
goto bail;
}
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
if (ipath_eeprom_read(dd, pos, tmp, count)) {
ipath_dev_err(dd, "failed to read from flash\n");
ret = -ENXIO;
@@ -357,19 +356,16 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
pos = *ppos;
- if ( pos < 0) {
+ if (pos != 0) {
ret = -EINVAL;
goto bail;
}
- if (pos >= sizeof(struct ipath_flash)) {
- ret = 0;
+ if (count != sizeof(struct ipath_flash)) {
+ ret = -EINVAL;
goto bail;
}
- if (count > sizeof(struct ipath_flash) - pos)
- count = sizeof(struct ipath_flash) - pos;
-
tmp = kmalloc(count, GFP_KERNEL);
if (!tmp) {
ret = -ENOMEM;
@@ -381,7 +377,7 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
goto bail_tmp;
}
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
if (ipath_eeprom_write(dd, pos, tmp, count)) {
ret = -ENXIO;
ipath_dev_err(dd, "failed to write to flash\n");
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index bf2455a6d56..9e4e8d4c6e2 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -252,8 +252,8 @@ static const struct ipath_cregs ipath_ht_cregs = {
};
/* kr_intstatus, kr_intclear, kr_intmask bits */
-#define INFINIPATH_I_RCVURG_MASK 0x1FF
-#define INFINIPATH_I_RCVAVAIL_MASK 0x1FF
+#define INFINIPATH_I_RCVURG_MASK ((1U<<9)-1)
+#define INFINIPATH_I_RCVAVAIL_MASK ((1U<<9)-1)
/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
#define INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT 0
@@ -338,7 +338,7 @@ static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs,
if (crcbits) {
u16 ctrl0, ctrl1;
snprintf(bitsmsg, sizeof bitsmsg,
- "[HT%s lane %s CRC (%llx); ignore till reload]",
+ "[HT%s lane %s CRC (%llx); powercycle to completely clear]",
!(crcbits & _IPATH_HTLINK1_CRCBITS) ?
"0 (A)" : (!(crcbits & _IPATH_HTLINK0_CRCBITS)
? "1 (B)" : "0+1 (A+B)"),
@@ -389,17 +389,28 @@ static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs,
_IPATH_HTLINK1_CRCBITS)));
}
+/* 6110 specific hardware errors... */
+static const struct ipath_hwerror_msgs ipath_6110_hwerror_msgs[] = {
+ INFINIPATH_HWE_MSG(HTCBUSIREQPARITYERR, "HTC Ireq Parity"),
+ INFINIPATH_HWE_MSG(HTCBUSTREQPARITYERR, "HTC Treq Parity"),
+ INFINIPATH_HWE_MSG(HTCBUSTRESPPARITYERR, "HTC Tresp Parity"),
+ INFINIPATH_HWE_MSG(HTCMISCERR5, "HT core Misc5"),
+ INFINIPATH_HWE_MSG(HTCMISCERR6, "HT core Misc6"),
+ INFINIPATH_HWE_MSG(HTCMISCERR7, "HT core Misc7"),
+ INFINIPATH_HWE_MSG(RXDSYNCMEMPARITYERR, "Rx Dsync"),
+ INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
+};
+
/**
- * ipath_ht_handle_hwerrors - display hardware errors
+ * ipath_ht_handle_hwerrors - display hardware errors.
* @dd: the infinipath device
* @msg: the output buffer
* @msgl: the size of the output buffer
*
- * Use same msg buffer as regular errors to avoid
- * excessive stack use. Most hardware errors are catastrophic, but for
- * right now, we'll print them and continue.
- * We reuse the same message buffer as ipath_handle_errors() to avoid
- * excessive stack usage.
+ * Use same msg buffer as regular errors to avoid excessive stack
+ * use. Most hardware errors are catastrophic, but for right now,
+ * we'll print them and continue. We reuse the same message buffer as
+ * ipath_handle_errors() to avoid excessive stack usage.
*/
static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
size_t msgl)
@@ -440,19 +451,49 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
* make sure we get this much out, unless told to be quiet,
* or it's occurred within the last 5 seconds
*/
- if ((hwerrs & ~dd->ipath_lasthwerror) ||
+ if ((hwerrs & ~(dd->ipath_lasthwerror |
+ ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) ||
(ipath_debug & __IPATH_VERBDBG))
dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
"(cleared)\n", (unsigned long long) hwerrs);
dd->ipath_lasthwerror |= hwerrs;
- if (hwerrs & ~infinipath_hwe_bitsextant)
+ if (hwerrs & ~dd->ipath_hwe_bitsextant)
ipath_dev_err(dd, "hwerror interrupt with unknown errors "
"%llx set\n", (unsigned long long)
- (hwerrs & ~infinipath_hwe_bitsextant));
+ (hwerrs & ~dd->ipath_hwe_bitsextant));
ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
if (ctrl & INFINIPATH_C_FREEZEMODE) {
+ /*
+ * parity errors in send memory are recoverable,
+ * just cancel the send (if indicated in * sendbuffererror),
+ * count the occurrence, unfreeze (if no other handled
+ * hardware error bits are set), and continue. They can
+ * occur if a processor speculative read is done to the PIO
+ * buffer while we are sending a packet, for example.
+ */
+ if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
+ ipath_stats.sps_txeparity++;
+ ipath_dbg("Recovering from TXE parity error (%llu), "
+ "hwerrstatus=%llx\n",
+ (unsigned long long) ipath_stats.sps_txeparity,
+ (unsigned long long) hwerrs);
+ ipath_disarm_senderrbufs(dd);
+ hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT);
+ if (!hwerrs) { /* else leave in freeze mode */
+ ipath_write_kreg(dd,
+ dd->ipath_kregs->kr_control,
+ dd->ipath_control);
+ return;
+ }
+ }
if (hwerrs) {
/*
* if any set that we aren't ignoring; only
@@ -499,44 +540,16 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
bits);
strlcat(msg, bitsmsg, msgl);
}
- if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_RXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
- if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_TXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
- if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR)
- strlcat(msg, "[IB2IPATH Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR)
- strlcat(msg, "[IPATH2IB Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCBUSIREQPARITYERR)
- strlcat(msg, "[HTC Ireq Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCBUSTREQPARITYERR)
- strlcat(msg, "[HTC Treq Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCBUSTRESPPARITYERR)
- strlcat(msg, "[HTC Tresp Parity]", msgl);
+
+ ipath_format_hwerrors(hwerrs,
+ ipath_6110_hwerror_msgs,
+ sizeof(ipath_6110_hwerror_msgs) /
+ sizeof(ipath_6110_hwerror_msgs[0]),
+ msg, msgl);
if (hwerrs & (_IPATH_HTLINK0_CRCBITS | _IPATH_HTLINK1_CRCBITS))
hwerr_crcbits(dd, hwerrs, msg, msgl);
- if (hwerrs & INFINIPATH_HWE_HTCMISCERR5)
- strlcat(msg, "[HT core Misc5]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCMISCERR6)
- strlcat(msg, "[HT core Misc6]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCMISCERR7)
- strlcat(msg, "[HT core Misc7]", msgl);
if (hwerrs & INFINIPATH_HWE_MEMBISTFAILED) {
strlcat(msg, "[Memory BIST test failed, InfiniPath hardware unusable]",
msgl);
@@ -573,11 +586,6 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
dd->ipath_hwerrmask);
}
- if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR)
- strlcat(msg, "[Rx Dsync]", msgl);
- if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED)
- strlcat(msg, "[SerDes PLL]", msgl);
-
ipath_dev_err(dd, "%s hardware error\n", msg);
if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg)
/*
@@ -742,7 +750,6 @@ static int ipath_setup_ht_reset(struct ipath_devdata *dd)
return 0;
}
-#define HT_CAPABILITY_ID 0x08 /* HT capabilities not defined in kernel */
#define HT_INTR_DISC_CONFIG 0x80 /* HT interrupt and discovery cap */
#define HT_INTR_REG_INDEX 2 /* intconfig requires indirect accesses */
@@ -973,7 +980,7 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
* do this early, before we ever enable errors or hardware errors,
* mostly to avoid causing the chip to enter freeze mode.
*/
- pos = pci_find_capability(pdev, HT_CAPABILITY_ID);
+ pos = pci_find_capability(pdev, PCI_CAP_ID_HT);
if (!pos) {
ipath_dev_err(dd, "Couldn't find HyperTransport "
"capability; no interrupts\n");
@@ -996,7 +1003,7 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
else if (cap_type == HT_INTR_DISC_CONFIG)
ihandler = set_int_handler(dd, pdev, pos);
} while ((pos = pci_find_next_capability(pdev, pos,
- HT_CAPABILITY_ID)));
+ PCI_CAP_ID_HT)));
if (!ihandler) {
ipath_dev_err(dd, "Couldn't find interrupt handler in "
@@ -1081,21 +1088,21 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl);
}
-static void ipath_init_ht_variables(void)
+static void ipath_init_ht_variables(struct ipath_devdata *dd)
{
- ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
- ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
- ipath_gpio_sda = IPATH_GPIO_SDA;
- ipath_gpio_scl = IPATH_GPIO_SCL;
+ dd->ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
+ dd->ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
+ dd->ipath_gpio_sda = IPATH_GPIO_SDA;
+ dd->ipath_gpio_scl = IPATH_GPIO_SCL;
- infinipath_i_bitsextant =
+ dd->ipath_i_bitsextant =
(INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) |
(INFINIPATH_I_RCVAVAIL_MASK <<
INFINIPATH_I_RCVAVAIL_SHIFT) |
INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT |
INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO;
- infinipath_e_bitsextant =
+ dd->ipath_e_bitsextant =
INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC |
INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN |
INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN |
@@ -1113,7 +1120,7 @@ static void ipath_init_ht_variables(void)
INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET |
INFINIPATH_E_HARDWARE;
- infinipath_hwe_bitsextant =
+ dd->ipath_hwe_bitsextant =
(INFINIPATH_HWE_HTCMEMPARITYERR_MASK <<
INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT) |
(INFINIPATH_HWE_TXEMEMPARITYERR_MASK <<
@@ -1142,8 +1149,8 @@ static void ipath_init_ht_variables(void)
INFINIPATH_HWE_IBCBUSTOSPCPARITYERR |
INFINIPATH_HWE_IBCBUSFRSPCPARITYERR;
- infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
- infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+ dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
+ dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
}
/**
@@ -1608,5 +1615,5 @@ void ipath_init_iba6110_funcs(struct ipath_devdata *dd)
* do very early init that is needed before ipath_f_bus is
* called
*/
- ipath_init_ht_variables();
+ ipath_init_ht_variables(dd);
}
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c
index d86516d23df..a72ab9de386 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -263,8 +263,8 @@ static const struct ipath_cregs ipath_pe_cregs = {
};
/* kr_intstatus, kr_intclear, kr_intmask bits */
-#define INFINIPATH_I_RCVURG_MASK 0x1F
-#define INFINIPATH_I_RCVAVAIL_MASK 0x1F
+#define INFINIPATH_I_RCVURG_MASK ((1U<<5)-1)
+#define INFINIPATH_I_RCVAVAIL_MASK ((1U<<5)-1)
/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
#define INFINIPATH_HWE_PCIEMEMPARITYERR_MASK 0x000000000000003fULL
@@ -294,6 +294,33 @@ static const struct ipath_cregs ipath_pe_cregs = {
#define IPATH_GPIO_SCL (1ULL << \
(_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+/*
+ * Rev2 silicon allows suppressing check for ArmLaunch errors.
+ * this can speed up short packet sends on systems that do
+ * not guaranteee write-order.
+ */
+#define INFINIPATH_XGXS_SUPPRESS_ARMLAUNCH_ERR (1ULL<<63)
+
+/* 6120 specific hardware errors... */
+static const struct ipath_hwerror_msgs ipath_6120_hwerror_msgs[] = {
+ INFINIPATH_HWE_MSG(PCIEPOISONEDTLP, "PCIe Poisoned TLP"),
+ INFINIPATH_HWE_MSG(PCIECPLTIMEOUT, "PCIe completion timeout"),
+ /*
+ * In practice, it's unlikely wthat we'll see PCIe PLL, or bus
+ * parity or memory parity error failures, because most likely we
+ * won't be able to talk to the core of the chip. Nonetheless, we
+ * might see them, if they are in parts of the PCIe core that aren't
+ * essential.
+ */
+ INFINIPATH_HWE_MSG(PCIE1PLLFAILED, "PCIePLL1"),
+ INFINIPATH_HWE_MSG(PCIE0PLLFAILED, "PCIePLL0"),
+ INFINIPATH_HWE_MSG(PCIEBUSPARITYXTLH, "PCIe XTLH core parity"),
+ INFINIPATH_HWE_MSG(PCIEBUSPARITYXADM, "PCIe ADM TX core parity"),
+ INFINIPATH_HWE_MSG(PCIEBUSPARITYRADM, "PCIe ADM RX core parity"),
+ INFINIPATH_HWE_MSG(RXDSYNCMEMPARITYERR, "Rx Dsync"),
+ INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
+};
+
/**
* ipath_pe_handle_hwerrors - display hardware errors.
* @dd: the infinipath device
@@ -343,19 +370,49 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
* make sure we get this much out, unless told to be quiet,
* or it's occurred within the last 5 seconds
*/
- if ((hwerrs & ~dd->ipath_lasthwerror) ||
+ if ((hwerrs & ~(dd->ipath_lasthwerror |
+ ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) ||
(ipath_debug & __IPATH_VERBDBG))
dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
"(cleared)\n", (unsigned long long) hwerrs);
dd->ipath_lasthwerror |= hwerrs;
- if (hwerrs & ~infinipath_hwe_bitsextant)
+ if (hwerrs & ~dd->ipath_hwe_bitsextant)
ipath_dev_err(dd, "hwerror interrupt with unknown errors "
"%llx set\n", (unsigned long long)
- (hwerrs & ~infinipath_hwe_bitsextant));
+ (hwerrs & ~dd->ipath_hwe_bitsextant));
ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
if (ctrl & INFINIPATH_C_FREEZEMODE) {
+ /*
+ * parity errors in send memory are recoverable,
+ * just cancel the send (if indicated in * sendbuffererror),
+ * count the occurrence, unfreeze (if no other handled
+ * hardware error bits are set), and continue. They can
+ * occur if a processor speculative read is done to the PIO
+ * buffer while we are sending a packet, for example.
+ */
+ if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
+ ipath_stats.sps_txeparity++;
+ ipath_dbg("Recovering from TXE parity error (%llu), "
+ "hwerrstatus=%llx\n",
+ (unsigned long long) ipath_stats.sps_txeparity,
+ (unsigned long long) hwerrs);
+ ipath_disarm_senderrbufs(dd);
+ hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT);
+ if (!hwerrs) { /* else leave in freeze mode */
+ ipath_write_kreg(dd,
+ dd->ipath_kregs->kr_control,
+ dd->ipath_control);
+ return;
+ }
+ }
if (hwerrs) {
/*
* if any set that we aren't ignoring only make the
@@ -379,9 +436,8 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
} else {
ipath_dbg("Clearing freezemode on ignored hardware "
"error\n");
- ctrl &= ~INFINIPATH_C_FREEZEMODE;
ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
- ctrl);
+ dd->ipath_control);
}
}
@@ -396,24 +452,13 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
dd->ipath_hwerrmask);
}
- if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_RXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
- if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_TXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
+
+ ipath_format_hwerrors(hwerrs,
+ ipath_6120_hwerror_msgs,
+ sizeof(ipath_6120_hwerror_msgs)/
+ sizeof(ipath_6120_hwerror_msgs[0]),
+ msg, msgl);
+
if (hwerrs & (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK
<< INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT)) {
bits = (u32) ((hwerrs >>
@@ -423,10 +468,6 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
"[PCIe Mem Parity Errs %x] ", bits);
strlcat(msg, bitsmsg, msgl);
}
- if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR)
- strlcat(msg, "[IB2IPATH Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR)
- strlcat(msg, "[IPATH2IB Parity]", msgl);
#define _IPATH_PLL_FAIL (INFINIPATH_HWE_COREPLL_FBSLIP | \
INFINIPATH_HWE_COREPLL_RFSLIP )
@@ -452,34 +493,6 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
dd->ipath_hwerrmask);
}
- if (hwerrs & INFINIPATH_HWE_PCIEPOISONEDTLP)
- strlcat(msg, "[PCIe Poisoned TLP]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIECPLTIMEOUT)
- strlcat(msg, "[PCIe completion timeout]", msgl);
-
- /*
- * In practice, it's unlikely wthat we'll see PCIe PLL, or bus
- * parity or memory parity error failures, because most likely we
- * won't be able to talk to the core of the chip. Nonetheless, we
- * might see them, if they are in parts of the PCIe core that aren't
- * essential.
- */
- if (hwerrs & INFINIPATH_HWE_PCIE1PLLFAILED)
- strlcat(msg, "[PCIePLL1]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIE0PLLFAILED)
- strlcat(msg, "[PCIePLL0]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXTLH)
- strlcat(msg, "[PCIe XTLH core parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXADM)
- strlcat(msg, "[PCIe ADM TX core parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYRADM)
- strlcat(msg, "[PCIe ADM RX core parity]", msgl);
-
- if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR)
- strlcat(msg, "[Rx Dsync]", msgl);
- if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED)
- strlcat(msg, "[SerDes PLL]", msgl);
-
ipath_dev_err(dd, "%s hardware error\n", msg);
if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) {
/*
@@ -525,6 +538,9 @@ static int ipath_pe_boardname(struct ipath_devdata *dd, char *name,
case 5:
n = "InfiniPath_QMH7140";
break;
+ case 6:
+ n = "InfiniPath_QLE7142";
+ break;
default:
ipath_dev_err(dd,
"Don't yet know about board with ID %u\n",
@@ -571,9 +587,12 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd)
if (!dd->ipath_boardrev) // no PLL for Emulator
val &= ~INFINIPATH_HWE_SERDESPLLFAILED;
- /* workaround bug 9460 in internal interface bus parity checking */
- val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM;
-
+ if (dd->ipath_minrev < 2) {
+ /* workaround bug 9460 in internal interface bus parity
+ * checking. Fixed (HW bug 9490) in Rev2.
+ */
+ val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM;
+ }
dd->ipath_hwerrmask = val;
}
@@ -583,8 +602,8 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd)
*/
static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
{
- u64 val, tmp, config1;
- int ret = 0, change = 0;
+ u64 val, tmp, config1, prev_val;
+ int ret = 0;
ipath_dbg("Trying to bringup serdes\n");
@@ -641,6 +660,7 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
+ prev_val = val;
if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) &
INFINIPATH_XGXS_MDIOADDR_MASK) != 3) {
val &=
@@ -648,11 +668,9 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
INFINIPATH_XGXS_MDIOADDR_SHIFT);
/* MDIO address 3 */
val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT;
- change = 1;
}
if (val & INFINIPATH_XGXS_RESET) {
val &= ~INFINIPATH_XGXS_RESET;
- change = 1;
}
if (((val >> INFINIPATH_XGXS_RX_POL_SHIFT) &
INFINIPATH_XGXS_RX_POL_MASK) != dd->ipath_rx_pol_inv ) {
@@ -661,9 +679,19 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
INFINIPATH_XGXS_RX_POL_SHIFT);
val |= dd->ipath_rx_pol_inv <<
INFINIPATH_XGXS_RX_POL_SHIFT;
- change = 1;
}
- if (change)
+ if (dd->ipath_minrev >= 2) {
+ /* Rev 2. can tolerate multiple writes to PBC, and
+ * allowing them can provide lower latency on some
+ * CPUs, but this feature is off by default, only
+ * turned on by setting D63 of XGXSconfig reg.
+ * May want to make this conditional more
+ * fine-grained in future. This is not exactly
+ * related to XGXS, but where the bit ended up.
+ */
+ val |= INFINIPATH_XGXS_SUPPRESS_ARMLAUNCH_ERR;
+ }
+ if (val != prev_val)
ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val);
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
@@ -717,9 +745,25 @@ static void ipath_pe_quiet_serdes(struct ipath_devdata *dd)
ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
}
-/* this is not yet needed on this chip, so just return 0. */
static int ipath_pe_intconfig(struct ipath_devdata *dd)
{
+ u64 val;
+ u32 chiprev;
+
+ /*
+ * If the chip supports added error indication via GPIO pins,
+ * enable interrupts on those bits so the interrupt routine
+ * can count the events. Also set flag so interrupt routine
+ * can know they are expected.
+ */
+ chiprev = dd->ipath_revision >> INFINIPATH_R_CHIPREVMINOR_SHIFT;
+ if ((chiprev & INFINIPATH_R_CHIPREVMINOR_MASK) > 1) {
+ /* Rev2+ reports extra errors via internal GPIO pins */
+ dd->ipath_flags |= IPATH_GPIO_ERRINTRS;
+ val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask);
+ val |= IPATH_GPIO_ERRINTR_MASK;
+ ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val);
+ }
return 0;
}
@@ -853,21 +897,23 @@ static int ipath_setup_pe_config(struct ipath_devdata *dd,
return 0;
}
-static void ipath_init_pe_variables(void)
+static void ipath_init_pe_variables(struct ipath_devdata *dd)
{
/*
* bits for selecting i2c direction and values,
* used for I2C serial flash
*/
- ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
- ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
- ipath_gpio_sda = IPATH_GPIO_SDA;
- ipath_gpio_scl = IPATH_GPIO_SCL;
+ dd->ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
+ dd->ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
+ dd->ipath_gpio_sda = IPATH_GPIO_SDA;
+ dd->ipath_gpio_scl = IPATH_GPIO_SCL;
/* variables for sanity checking interrupt and errors */
- infinipath_hwe_bitsextant =
+ dd->ipath_hwe_bitsextant =
(INFINIPATH_HWE_RXEMEMPARITYERR_MASK <<
INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) |
+ (INFINIPATH_HWE_TXEMEMPARITYERR_MASK <<
+ INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) |
(INFINIPATH_HWE_PCIEMEMPARITYERR_MASK <<
INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) |
INFINIPATH_HWE_PCIE1PLLFAILED |
@@ -883,13 +929,13 @@ static void ipath_init_pe_variables(void)
INFINIPATH_HWE_SERDESPLLFAILED |
INFINIPATH_HWE_IBCBUSTOSPCPARITYERR |
INFINIPATH_HWE_IBCBUSFRSPCPARITYERR;
- infinipath_i_bitsextant =
+ dd->ipath_i_bitsextant =
(INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) |
(INFINIPATH_I_RCVAVAIL_MASK <<
INFINIPATH_I_RCVAVAIL_SHIFT) |
INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT |
INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO;
- infinipath_e_bitsextant =
+ dd->ipath_e_bitsextant =
INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC |
INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN |
INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN |
@@ -907,8 +953,8 @@ static void ipath_init_pe_variables(void)
INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET |
INFINIPATH_E_HARDWARE;
- infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
- infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+ dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
+ dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
}
/* setup the MSI stuff again after a reset. I'd like to just call
@@ -1082,6 +1128,45 @@ static void ipath_pe_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr,
mmiowb();
spin_unlock_irqrestore(&dd->ipath_tid_lock, flags);
}
+/**
+ * ipath_pe_put_tid_2 - write a TID in chip, Revision 2 or higher
+ * @dd: the infinipath device
+ * @tidptr: pointer to the expected TID (in chip) to udpate
+ * @tidtype: 0 for eager, 1 for expected
+ * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing
+ *
+ * This exists as a separate routine to allow for selection of the
+ * appropriate "flavor". The static calls in cleanup just use the
+ * revision-agnostic form, as they are not performance critical.
+ */
+static void ipath_pe_put_tid_2(struct ipath_devdata *dd, u64 __iomem *tidptr,
+ u32 type, unsigned long pa)
+{
+ u32 __iomem *tidp32 = (u32 __iomem *)tidptr;
+
+ if (pa != dd->ipath_tidinvalid) {
+ if (pa & ((1U << 11) - 1)) {
+ dev_info(&dd->pcidev->dev, "BUG: physaddr %lx "
+ "not 2KB aligned!\n", pa);
+ return;
+ }
+ pa >>= 11;
+ /* paranoia check */
+ if (pa & (7<<29))
+ ipath_dev_err(dd,
+ "BUG: Physical page address 0x%lx "
+ "has bits set in 31-29\n", pa);
+
+ if (type == 0)
+ pa |= dd->ipath_tidtemplate;
+ else /* for now, always full 4KB page */
+ pa |= 2 << 29;
+ }
+ if (dd->ipath_kregbase)
+ writel(pa, tidp32);
+ mmiowb();
+}
+
/**
* ipath_pe_clear_tid - clear all TID entries for a port, expected and eager
@@ -1203,7 +1288,7 @@ int __attribute__((weak)) ipath_unordered_wc(void)
/**
* ipath_init_pe_get_base_info - set chip-specific flags for user code
- * @dd: the infinipath device
+ * @pd: the infinipath port
* @kbase: ipath_base_info pointer
*
* We set the PCIE flag because the lower bandwidth on PCIe vs
@@ -1212,6 +1297,7 @@ int __attribute__((weak)) ipath_unordered_wc(void)
static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase)
{
struct ipath_base_info *kinfo = kbase;
+ struct ipath_devdata *dd;
if (ipath_unordered_wc()) {
kinfo->spi_runtime_flags |= IPATH_RUNTIME_FORCE_WC_ORDER;
@@ -1220,8 +1306,20 @@ static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase)
else
ipath_cdbg(PROC, "Not Intel processor, WC ordered\n");
- kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE;
+ if (pd == NULL)
+ goto done;
+ dd = pd->port_dd;
+
+ if (dd != NULL && dd->ipath_minrev >= 2) {
+ ipath_cdbg(PROC, "IBA6120 Rev2, allow multiple PBC write\n");
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_PBC_REWRITE;
+ ipath_cdbg(PROC, "IBA6120 Rev2, allow loose DMA alignment\n");
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_LOOSE_DMA_ALIGN;
+ }
+
+done:
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE;
return 0;
}
@@ -1244,7 +1342,10 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd)
dd->ipath_f_quiet_serdes = ipath_pe_quiet_serdes;
dd->ipath_f_bringup_serdes = ipath_pe_bringup_serdes;
dd->ipath_f_clear_tids = ipath_pe_clear_tids;
- dd->ipath_f_put_tid = ipath_pe_put_tid;
+ if (dd->ipath_minrev >= 2)
+ dd->ipath_f_put_tid = ipath_pe_put_tid_2;
+ else
+ dd->ipath_f_put_tid = ipath_pe_put_tid;
dd->ipath_f_cleanup = ipath_setup_pe_cleanup;
dd->ipath_f_setextled = ipath_setup_pe_setextled;
dd->ipath_f_get_base_info = ipath_pe_get_base_info;
@@ -1259,6 +1360,6 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd)
dd->ipath_kregs = &ipath_pe_kregs;
dd->ipath_cregs = &ipath_pe_cregs;
- ipath_init_pe_variables();
+ ipath_init_pe_variables(dd);
}
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 44669dc2e22..d819cca524c 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -88,13 +88,13 @@ MODULE_PARM_DESC(kpiobufs, "Set number of PIO buffers for driver");
static int create_port0_egr(struct ipath_devdata *dd)
{
unsigned e, egrcnt;
- struct sk_buff **skbs;
+ struct ipath_skbinfo *skbinfo;
int ret;
egrcnt = dd->ipath_rcvegrcnt;
- skbs = vmalloc(sizeof(*dd->ipath_port0_skbs) * egrcnt);
- if (skbs == NULL) {
+ skbinfo = vmalloc(sizeof(*dd->ipath_port0_skbinfo) * egrcnt);
+ if (skbinfo == NULL) {
ipath_dev_err(dd, "allocation error for eager TID "
"skb array\n");
ret = -ENOMEM;
@@ -109,13 +109,13 @@ static int create_port0_egr(struct ipath_devdata *dd)
* 4 bytes so that the data buffer stays word aligned.
* See ipath_kreceive() for more details.
*/
- skbs[e] = ipath_alloc_skb(dd, GFP_KERNEL);
- if (!skbs[e]) {
+ skbinfo[e].skb = ipath_alloc_skb(dd, GFP_KERNEL);
+ if (!skbinfo[e].skb) {
ipath_dev_err(dd, "SKB allocation error for "
"eager TID %u\n", e);
while (e != 0)
- dev_kfree_skb(skbs[--e]);
- vfree(skbs);
+ dev_kfree_skb(skbinfo[--e].skb);
+ vfree(skbinfo);
ret = -ENOMEM;
goto bail;
}
@@ -124,14 +124,17 @@ static int create_port0_egr(struct ipath_devdata *dd)
* After loop above, so we can test non-NULL to see if ready
* to use at receive, etc.
*/
- dd->ipath_port0_skbs = skbs;
+ dd->ipath_port0_skbinfo = skbinfo;
for (e = 0; e < egrcnt; e++) {
- unsigned long phys =
- virt_to_phys(dd->ipath_port0_skbs[e]->data);
+ dd->ipath_port0_skbinfo[e].phys =
+ ipath_map_single(dd->pcidev,
+ dd->ipath_port0_skbinfo[e].skb->data,
+ dd->ipath_ibmaxlen, PCI_DMA_FROMDEVICE);
dd->ipath_f_put_tid(dd, e + (u64 __iomem *)
((char __iomem *) dd->ipath_kregbase +
- dd->ipath_rcvegrbase), 0, phys);
+ dd->ipath_rcvegrbase), 0,
+ dd->ipath_port0_skbinfo[e].phys);
}
ret = 0;
@@ -432,16 +435,33 @@ done:
*/
static void init_shadow_tids(struct ipath_devdata *dd)
{
- dd->ipath_pageshadow = (struct page **)
- vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+ struct page **pages;
+ dma_addr_t *addrs;
+
+ pages = vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt *
sizeof(struct page *));
- if (!dd->ipath_pageshadow)
+ if (!pages) {
ipath_dev_err(dd, "failed to allocate shadow page * "
"array, no expected sends!\n");
- else
- memset(dd->ipath_pageshadow, 0,
- dd->ipath_cfgports * dd->ipath_rcvtidcnt *
- sizeof(struct page *));
+ dd->ipath_pageshadow = NULL;
+ return;
+ }
+
+ addrs = vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+ sizeof(dma_addr_t));
+ if (!addrs) {
+ ipath_dev_err(dd, "failed to allocate shadow dma handle "
+ "array, no expected sends!\n");
+ vfree(dd->ipath_pageshadow);
+ dd->ipath_pageshadow = NULL;
+ return;
+ }
+
+ memset(pages, 0, dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+ sizeof(struct page *));
+
+ dd->ipath_pageshadow = pages;
+ dd->ipath_physshadow = addrs;
}
static void enable_chip(struct ipath_devdata *dd,
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c
index 49bf7bb15b0..6bee53ce5f3 100644
--- a/drivers/infiniband/hw/ipath/ipath_intr.c
+++ b/drivers/infiniband/hw/ipath/ipath_intr.c
@@ -37,6 +37,50 @@
#include "ipath_verbs.h"
#include "ipath_common.h"
+/*
+ * Called when we might have an error that is specific to a particular
+ * PIO buffer, and may need to cancel that buffer, so it can be re-used.
+ */
+void ipath_disarm_senderrbufs(struct ipath_devdata *dd)
+{
+ u32 piobcnt;
+ unsigned long sbuf[4];
+ /*
+ * it's possible that sendbuffererror could have bits set; might
+ * have already done this as a result of hardware error handling
+ */
+ piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
+ /* read these before writing errorclear */
+ sbuf[0] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror);
+ sbuf[1] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror + 1);
+ if (piobcnt > 128) {
+ sbuf[2] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror + 2);
+ sbuf[3] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror + 3);
+ }
+
+ if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {
+ int i;
+ if (ipath_debug & (__IPATH_PKTDBG|__IPATH_DBG)) {
+ __IPATH_DBG_WHICH(__IPATH_PKTDBG|__IPATH_DBG,
+ "SendbufErrs %lx %lx", sbuf[0],
+ sbuf[1]);
+ if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128)
+ printk(" %lx %lx ", sbuf[2], sbuf[3]);
+ printk("\n");
+ }
+
+ for (i = 0; i < piobcnt; i++)
+ if (test_bit(i, sbuf))
+ ipath_disarm_piobufs(dd, i, 1);
+ dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */
+ }
+}
+
+
/* These are all rcv-related errors which we want to count for stats */
#define E_SUM_PKTERRS \
(INFINIPATH_E_RHDRLEN | INFINIPATH_E_RBADTID | \
@@ -68,53 +112,9 @@
static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs)
{
- unsigned long sbuf[4];
u64 ignore_this_time = 0;
- u32 piobcnt;
- /* if possible that sendbuffererror could be valid */
- piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
- /* read these before writing errorclear */
- sbuf[0] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror);
- sbuf[1] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror + 1);
- if (piobcnt > 128) {
- sbuf[2] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror + 2);
- sbuf[3] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror + 3);
- }
-
- if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {
- int i;
-
- ipath_cdbg(PKT, "SendbufErrs %lx %lx ", sbuf[0], sbuf[1]);
- if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128)
- printk("%lx %lx ", sbuf[2], sbuf[3]);
- for (i = 0; i < piobcnt; i++) {
- if (test_bit(i, sbuf)) {
- u32 __iomem *piobuf;
- if (i < dd->ipath_piobcnt2k)
- piobuf = (u32 __iomem *)
- (dd->ipath_pio2kbase +
- i * dd->ipath_palign);
- else
- piobuf = (u32 __iomem *)
- (dd->ipath_pio4kbase +
- (i - dd->ipath_piobcnt2k) *
- dd->ipath_4kalign);
-
- ipath_cdbg(PKT,
- "PIObuf[%u] @%p pbc is %x; ",
- i, piobuf, readl(piobuf));
-
- ipath_disarm_piobufs(dd, i, 1);
- }
- }
- if (ipath_debug & __IPATH_PKTDBG)
- printk("\n");
- }
+ ipath_disarm_senderrbufs(dd);
if ((errs & E_SUM_LINK_PKTERRS) &&
!(dd->ipath_flags & IPATH_LINKACTIVE)) {
/*
@@ -132,6 +132,82 @@ static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs)
return ignore_this_time;
}
+/* generic hw error messages... */
+#define INFINIPATH_HWE_TXEMEMPARITYERR_MSG(a) \
+ { \
+ .mask = ( INFINIPATH_HWE_TXEMEMPARITYERR_##a << \
+ INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT ), \
+ .msg = "TXE " #a " Memory Parity" \
+ }
+#define INFINIPATH_HWE_RXEMEMPARITYERR_MSG(a) \
+ { \
+ .mask = ( INFINIPATH_HWE_RXEMEMPARITYERR_##a << \
+ INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT ), \
+ .msg = "RXE " #a " Memory Parity" \
+ }
+
+static const struct ipath_hwerror_msgs ipath_generic_hwerror_msgs[] = {
+ INFINIPATH_HWE_MSG(IBCBUSFRSPCPARITYERR, "IPATH2IB Parity"),
+ INFINIPATH_HWE_MSG(IBCBUSTOSPCPARITYERR, "IB2IPATH Parity"),
+
+ INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOBUF),
+ INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOPBC),
+ INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOLAUNCHFIFO),
+
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(RCVBUF),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(LOOKUPQ),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EAGERTID),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EXPTID),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(FLAGBUF),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(DATAINFO),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(HDRINFO),
+};
+
+/**
+ * ipath_format_hwmsg - format a single hwerror message
+ * @msg message buffer
+ * @msgl length of message buffer
+ * @hwmsg message to add to message buffer
+ */
+static void ipath_format_hwmsg(char *msg, size_t msgl, const char *hwmsg)
+{
+ strlcat(msg, "[", msgl);
+ strlcat(msg, hwmsg, msgl);
+ strlcat(msg, "]", msgl);
+}
+
+/**
+ * ipath_format_hwerrors - format hardware error messages for display
+ * @hwerrs hardware errors bit vector
+ * @hwerrmsgs hardware error descriptions
+ * @nhwerrmsgs number of hwerrmsgs
+ * @msg message buffer
+ * @msgl message buffer length
+ */
+void ipath_format_hwerrors(u64 hwerrs,
+ const struct ipath_hwerror_msgs *hwerrmsgs,
+ size_t nhwerrmsgs,
+ char *msg, size_t msgl)
+{
+ int i;
+ const int glen =
+ sizeof(ipath_generic_hwerror_msgs) /
+ sizeof(ipath_generic_hwerror_msgs[0]);
+
+ for (i=0; i<glen; i++) {
+ if (hwerrs & ipath_generic_hwerror_msgs[i].mask) {
+ ipath_format_hwmsg(msg, msgl,
+ ipath_generic_hwerror_msgs[i].msg);
+ }
+ }
+
+ for (i=0; i<nhwerrmsgs; i++) {
+ if (hwerrs & hwerrmsgs[i].mask) {
+ ipath_format_hwmsg(msg, msgl, hwerrmsgs[i].msg);
+ }
+ }
+}
+
/* return the strings for the most common link states */
static char *ib_linkstate(u32 linkstate)
{
@@ -404,10 +480,10 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
dd->ipath_f_handle_hwerrors(dd, msg, sizeof msg);
}
- if (!noprint && (errs & ~infinipath_e_bitsextant))
+ if (!noprint && (errs & ~dd->ipath_e_bitsextant))
ipath_dev_err(dd, "error interrupt with unknown errors "
"%llx set\n", (unsigned long long)
- (errs & ~infinipath_e_bitsextant));
+ (errs & ~dd->ipath_e_bitsextant));
if (errs & E_SUM_ERRS)
ignore_this_time = handle_e_sum_errs(dd, errs);
@@ -478,6 +554,14 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
~(INFINIPATH_E_HARDWARE |
INFINIPATH_E_IBSTATUSCHANGED);
}
+
+ /* likely due to cancel, so suppress */
+ if ((errs & (INFINIPATH_E_SPKTLEN | INFINIPATH_E_SPIOARMLAUNCH)) &&
+ dd->ipath_lastcancel > jiffies) {
+ ipath_dbg("Suppressed armlaunch/spktlen after error send cancel\n");
+ errs &= ~(INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SPKTLEN);
+ }
+
if (!errs)
return 0;
@@ -529,7 +613,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
* don't report same point multiple times,
* except kernel
*/
- tl = (u32) * pd->port_rcvhdrtail_kvaddr;
+ tl = *(u64 *) pd->port_rcvhdrtail_kvaddr;
if (tl == dd->ipath_lastrcvhdrqtails[i])
continue;
hd = ipath_read_ureg32(dd, ur_rcvhdrhead,
@@ -729,9 +813,9 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
int rcvdint = 0;
portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
- infinipath_i_rcvavail_mask)
+ dd->ipath_i_rcvavail_mask)
| ((istat >> INFINIPATH_I_RCVURG_SHIFT) &
- infinipath_i_rcvurg_mask);
+ dd->ipath_i_rcvurg_mask);
for (i = 1; i < dd->ipath_cfgports; i++) {
struct ipath_portdata *pd = dd->ipath_pd[i];
if (portr & (1 << i) && pd && pd->port_cnt &&
@@ -808,7 +892,7 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
if (oldhead != curtail) {
if (dd->ipath_flags & IPATH_GPIO_INTR) {
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
- (u64) (1 << 2));
+ (u64) (1 << IPATH_GPIO_PORT0_BIT));
istat = port0rbits | INFINIPATH_I_GPIO;
}
else
@@ -838,10 +922,10 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
if (unexpected)
unexpected = 0;
- if (unlikely(istat & ~infinipath_i_bitsextant))
+ if (unlikely(istat & ~dd->ipath_i_bitsextant))
ipath_dev_err(dd,
"interrupt with unknown interrupts %x set\n",
- istat & (u32) ~ infinipath_i_bitsextant);
+ istat & (u32) ~ dd->ipath_i_bitsextant);
else
ipath_cdbg(VERBOSE, "intr stat=0x%x\n", istat);
@@ -867,26 +951,80 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
if (istat & INFINIPATH_I_GPIO) {
/*
- * Packets are available in the port 0 rcv queue.
- * Eventually this needs to be generalized to check
- * IPATH_GPIO_INTR, and the specific GPIO bit, if
- * GPIO interrupts are used for anything else.
+ * GPIO interrupts fall in two broad classes:
+ * GPIO_2 indicates (on some HT4xx boards) that a packet
+ * has arrived for Port 0. Checking for this
+ * is controlled by flag IPATH_GPIO_INTR.
+ * GPIO_3..5 on IBA6120 Rev2 chips indicate errors
+ * that we need to count. Checking for this
+ * is controlled by flag IPATH_GPIO_ERRINTRS.
*/
- if (unlikely(!(dd->ipath_flags & IPATH_GPIO_INTR))) {
- u32 gpiostatus;
- gpiostatus = ipath_read_kreg32(
- dd, dd->ipath_kregs->kr_gpio_status);
- ipath_dbg("Unexpected GPIO interrupt bits %x\n",
- gpiostatus);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
- gpiostatus);
+ u32 gpiostatus;
+ u32 to_clear = 0;
+
+ gpiostatus = ipath_read_kreg32(
+ dd, dd->ipath_kregs->kr_gpio_status);
+ /* First the error-counter case.
+ */
+ if ((gpiostatus & IPATH_GPIO_ERRINTR_MASK) &&
+ (dd->ipath_flags & IPATH_GPIO_ERRINTRS)) {
+ /* want to clear the bits we see asserted. */
+ to_clear |= (gpiostatus & IPATH_GPIO_ERRINTR_MASK);
+
+ /*
+ * Count appropriately, clear bits out of our copy,
+ * as they have been "handled".
+ */
+ if (gpiostatus & (1 << IPATH_GPIO_RXUVL_BIT)) {
+ ipath_dbg("FlowCtl on UnsupVL\n");
+ dd->ipath_rxfc_unsupvl_errs++;
+ }
+ if (gpiostatus & (1 << IPATH_GPIO_OVRUN_BIT)) {
+ ipath_dbg("Overrun Threshold exceeded\n");
+ dd->ipath_overrun_thresh_errs++;
+ }
+ if (gpiostatus & (1 << IPATH_GPIO_LLI_BIT)) {
+ ipath_dbg("Local Link Integrity error\n");
+ dd->ipath_lli_errs++;
+ }
+ gpiostatus &= ~IPATH_GPIO_ERRINTR_MASK;
}
- else {
- /* Clear GPIO status bit 2 */
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
- (u64) (1 << 2));
+ /* Now the Port0 Receive case */
+ if ((gpiostatus & (1 << IPATH_GPIO_PORT0_BIT)) &&
+ (dd->ipath_flags & IPATH_GPIO_INTR)) {
+ /*
+ * GPIO status bit 2 is set, and we expected it.
+ * clear it and indicate in p0bits.
+ * This probably only happens if a Port0 pkt
+ * arrives at _just_ the wrong time, and we
+ * handle that by seting chk0rcv;
+ */
+ to_clear |= (1 << IPATH_GPIO_PORT0_BIT);
+ gpiostatus &= ~(1 << IPATH_GPIO_PORT0_BIT);
chk0rcv = 1;
}
+ if (unlikely(gpiostatus)) {
+ /*
+ * Some unexpected bits remain. If they could have
+ * caused the interrupt, complain and clear.
+ * MEA: this is almost certainly non-ideal.
+ * we should look into auto-disable of unexpected
+ * GPIO interrupts, possibly on a "three strikes"
+ * basis.
+ */
+ u32 mask;
+ mask = ipath_read_kreg32(
+ dd, dd->ipath_kregs->kr_gpio_mask);
+ if (mask & gpiostatus) {
+ ipath_dbg("Unexpected GPIO IRQ bits %x\n",
+ gpiostatus & mask);
+ to_clear |= (gpiostatus & mask);
+ }
+ }
+ if (to_clear) {
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
+ (u64) to_clear);
+ }
}
chk0rcv |= istat & port0rbits;
@@ -911,9 +1049,9 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
istat &= ~port0rbits;
}
- if (istat & ((infinipath_i_rcvavail_mask <<
+ if (istat & ((dd->ipath_i_rcvavail_mask <<
INFINIPATH_I_RCVAVAIL_SHIFT)
- | (infinipath_i_rcvurg_mask <<
+ | (dd->ipath_i_rcvurg_mask <<
INFINIPATH_I_RCVURG_SHIFT)))
handle_urcv(dd, istat);
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index a8a56276ff1..d7540b71b45 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -39,6 +39,8 @@
*/
#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
#include <asm/io.h>
#include "ipath_common.h"
@@ -62,7 +64,7 @@ struct ipath_portdata {
/* rcvhdrq base, needs mmap before useful */
void *port_rcvhdrq;
/* kernel virtual address where hdrqtail is updated */
- volatile __le64 *port_rcvhdrtail_kvaddr;
+ void *port_rcvhdrtail_kvaddr;
/*
* temp buffer for expected send setup, allocated at open, instead
* of each setup call
@@ -79,8 +81,8 @@ struct ipath_portdata {
dma_addr_t port_rcvhdrq_phys;
dma_addr_t port_rcvhdrqtailaddr_phys;
/*
- * number of opens on this instance (0 or 1; ignoring forks, dup,
- * etc. for now)
+ * number of opens (including slave subports) on this instance
+ * (ignoring forks, dup, etc. for now)
*/
int port_cnt;
/*
@@ -89,6 +91,10 @@ struct ipath_portdata {
*/
/* instead of calculating it */
unsigned port_port;
+ /* non-zero if port is being shared. */
+ u16 port_subport_cnt;
+ /* non-zero if port is being shared. */
+ u16 port_subport_id;
/* chip offset of PIO buffers for this port */
u32 port_piobufs;
/* how many alloc_pages() chunks in port_rcvegrbuf_pages */
@@ -121,6 +127,16 @@ struct ipath_portdata {
u16 port_pkeys[4];
/* so file ops can get at unit */
struct ipath_devdata *port_dd;
+ /* A page of memory for rcvhdrhead, rcvegrhead, rcvegrtail * N */
+ void *subport_uregbase;
+ /* An array of pages for the eager receive buffers * N */
+ void *subport_rcvegrbuf;
+ /* An array of pages for the eager header queue entries * N */
+ void *subport_rcvhdr_base;
+ /* The version of the library which opened this port */
+ u32 userversion;
+ /* Bitmask of active slaves */
+ u32 active_slaves;
};
struct sk_buff;
@@ -132,6 +148,11 @@ struct _ipath_layer {
void *l_arg;
};
+struct ipath_skbinfo {
+ struct sk_buff *skb;
+ dma_addr_t phys;
+};
+
struct ipath_devdata {
struct list_head ipath_list;
@@ -154,7 +175,7 @@ struct ipath_devdata {
/* ipath_cfgports pointers */
struct ipath_portdata **ipath_pd;
/* sk_buffs used by port 0 eager receive queue */
- struct sk_buff **ipath_port0_skbs;
+ struct ipath_skbinfo *ipath_port0_skbinfo;
/* kvirt address of 1st 2k pio buffer */
void __iomem *ipath_pio2kbase;
/* kvirt address of 1st 4k pio buffer */
@@ -315,12 +336,16 @@ struct ipath_devdata {
u8 ipath_ht_slave_off;
/* for write combining settings */
unsigned long ipath_wc_cookie;
+ unsigned long ipath_wc_base;
+ unsigned long ipath_wc_len;
/* ref count for each pkey */
atomic_t ipath_pkeyrefs[4];
/* shadow copy of all exptids physaddr; used only by funcsim */
u64 *ipath_tidsimshadow;
/* shadow copy of struct page *'s for exp tid pages */
struct page **ipath_pageshadow;
+ /* shadow copy of dma handles for exp tid pages */
+ dma_addr_t *ipath_physshadow;
/* lock to workaround chip bug 9437 */
spinlock_t ipath_tid_lock;
@@ -402,6 +427,9 @@ struct ipath_devdata {
unsigned long ipath_rcvctrl;
/* shadow kr_sendctrl */
unsigned long ipath_sendctrl;
+ /* ports waiting for PIOavail intr */
+ unsigned long ipath_portpiowait;
+ unsigned long ipath_lastcancel; /* to not count armlaunch after cancel */
/* value we put in kr_rcvhdrcnt */
u32 ipath_rcvhdrcnt;
@@ -465,8 +493,6 @@ struct ipath_devdata {
u32 ipath_htwidth;
/* HT speed (200,400,800,1000) from HT config */
u32 ipath_htspeed;
- /* ports waiting for PIOavail intr */
- unsigned long ipath_portpiowait;
/*
* number of sequential ibcstatus change for polling active/quiet
* (i.e., link not coming up).
@@ -510,8 +536,47 @@ struct ipath_devdata {
u32 ipath_lli_counter;
/* local link integrity errors */
u32 ipath_lli_errors;
+ /*
+ * Above counts only cases where _successive_ LocalLinkIntegrity
+ * errors were seen in the receive headers of kern-packets.
+ * Below are the three (monotonically increasing) counters
+ * maintained via GPIO interrupts on iba6120-rev2.
+ */
+ u32 ipath_rxfc_unsupvl_errs;
+ u32 ipath_overrun_thresh_errs;
+ u32 ipath_lli_errs;
+
+ /*
+ * Not all devices managed by a driver instance are the same
+ * type, so these fields must be per-device.
+ */
+ u64 ipath_i_bitsextant;
+ ipath_err_t ipath_e_bitsextant;
+ ipath_err_t ipath_hwe_bitsextant;
+
+ /*
+ * Below should be computable from number of ports,
+ * since they are never modified.
+ */
+ u32 ipath_i_rcvavail_mask;
+ u32 ipath_i_rcvurg_mask;
+
+ /*
+ * Register bits for selecting i2c direction and values, used for
+ * I2C serial flash.
+ */
+ u16 ipath_gpio_sda_num;
+ u16 ipath_gpio_scl_num;
+ u64 ipath_gpio_sda;
+ u64 ipath_gpio_scl;
};
+/* Private data for file operations */
+struct ipath_filedata {
+ struct ipath_portdata *pd;
+ unsigned subport;
+ unsigned tidcursor;
+};
extern struct list_head ipath_dev_list;
extern spinlock_t ipath_devs_lock;
extern struct ipath_devdata *ipath_lookup(int unit);
@@ -521,6 +586,7 @@ int ipath_enable_wc(struct ipath_devdata *dd);
void ipath_disable_wc(struct ipath_devdata *dd);
int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp);
void ipath_shutdown_device(struct ipath_devdata *);
+void ipath_disarm_senderrbufs(struct ipath_devdata *);
struct file_operations;
int ipath_cdev_init(int minor, char *name, struct file_operations *fops,
@@ -572,7 +638,11 @@ int ipath_set_lid(struct ipath_devdata *, u32, u8);
int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
/* for use in system calls, where we want to know device type, etc. */
-#define port_fp(fp) ((struct ipath_portdata *) (fp)->private_data)
+#define port_fp(fp) ((struct ipath_filedata *)(fp)->private_data)->pd
+#define subport_fp(fp) \
+ ((struct ipath_filedata *)(fp)->private_data)->subport
+#define tidcursor_fp(fp) \
+ ((struct ipath_filedata *)(fp)->private_data)->tidcursor
/*
* values for ipath_flags
@@ -612,6 +682,15 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
/* can miss port0 rx interrupts */
#define IPATH_POLL_RX_INTR 0x40000
#define IPATH_DISABLED 0x80000 /* administratively disabled */
+ /* Use GPIO interrupts for new counters */
+#define IPATH_GPIO_ERRINTRS 0x100000
+
+/* Bits in GPIO for the added interrupts */
+#define IPATH_GPIO_PORT0_BIT 2
+#define IPATH_GPIO_RXUVL_BIT 3
+#define IPATH_GPIO_OVRUN_BIT 4
+#define IPATH_GPIO_LLI_BIT 5
+#define IPATH_GPIO_ERRINTR_MASK 0x38
/* portdata flag bit offsets */
/* waiting for a packet to arrive */
@@ -799,6 +878,13 @@ int ipathfs_add_device(struct ipath_devdata *);
int ipathfs_remove_device(struct ipath_devdata *);
/*
+ * dma_addr wrappers - all 0's invalid for hw
+ */
+dma_addr_t ipath_map_page(struct pci_dev *, struct page *, unsigned long,
+ size_t, int);
+dma_addr_t ipath_map_single(struct pci_dev *, void *, size_t, int);
+
+/*
* Flush write combining store buffers (if present) and perform a write
* barrier.
*/
@@ -855,4 +941,20 @@ extern struct mutex ipath_mutex;
#endif /* _IPATH_DEBUGGING */
+/*
+ * this is used for formatting hw error messages...
+ */
+struct ipath_hwerror_msgs {
+ u64 mask;
+ const char *msg;
+};
+
+#define INFINIPATH_HWE_MSG(a, b) { .mask = INFINIPATH_HWE_##a, .msg = b }
+
+/* in ipath_intr.c... */
+void ipath_format_hwerrors(u64 hwerrs,
+ const struct ipath_hwerror_msgs *hwerrmsgs,
+ size_t nhwerrmsgs,
+ char *msg, size_t lmsg);
+
#endif /* _IPATH_KERNEL_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c
index ba1b93226ca..9a6cbd05adc 100644
--- a/drivers/infiniband/hw/ipath/ipath_keys.c
+++ b/drivers/infiniband/hw/ipath/ipath_keys.c
@@ -118,9 +118,10 @@ void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey)
* Check the IB SGE for validity and initialize our internal version
* of it.
*/
-int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
+int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
struct ib_sge *sge, int acc)
{
+ struct ipath_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table;
struct ipath_mregion *mr;
unsigned n, m;
size_t off;
@@ -140,7 +141,8 @@ int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
goto bail;
}
mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))];
- if (unlikely(mr == NULL || mr->lkey != sge->lkey)) {
+ if (unlikely(mr == NULL || mr->lkey != sge->lkey ||
+ qp->ibqp.pd != mr->pd)) {
ret = 0;
goto bail;
}
@@ -188,9 +190,10 @@ bail:
*
* Return 1 if successful, otherwise 0.
*/
-int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
+int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
u32 len, u64 vaddr, u32 rkey, int acc)
{
+ struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
struct ipath_lkey_table *rkt = &dev->lk_table;
struct ipath_sge *sge = &ss->sge;
struct ipath_mregion *mr;
@@ -214,7 +217,8 @@ int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
}
mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))];
- if (unlikely(mr == NULL || mr->lkey != rkey)) {
+ if (unlikely(mr == NULL || mr->lkey != rkey ||
+ qp->ibqp.pd != mr->pd)) {
ret = 0;
goto bail;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c
index 72d1db89db8..25908b02fbe 100644
--- a/drivers/infiniband/hw/ipath/ipath_mad.c
+++ b/drivers/infiniband/hw/ipath/ipath_mad.c
@@ -87,7 +87,8 @@ static int recv_subn_get_nodeinfo(struct ib_smp *smp,
struct ipath_devdata *dd = to_idev(ibdev)->dd;
u32 vendor, majrev, minrev;
- if (smp->attr_mod)
+ /* GUID 0 is illegal */
+ if (smp->attr_mod || (dd->ipath_guid == 0))
smp->status |= IB_SMP_INVALID_FIELD;
nip->base_version = 1;
@@ -131,10 +132,15 @@ static int recv_subn_get_guidinfo(struct ib_smp *smp,
* We only support one GUID for now. If this changes, the
* portinfo.guid_cap field needs to be updated too.
*/
- if (startgx == 0)
- /* The first is a copy of the read-only HW GUID. */
- *p = to_idev(ibdev)->dd->ipath_guid;
- else
+ if (startgx == 0) {
+ __be64 g = to_idev(ibdev)->dd->ipath_guid;
+ if (g == 0)
+ /* GUID 0 is illegal */
+ smp->status |= IB_SMP_INVALID_FIELD;
+ else
+ /* The first is a copy of the read-only HW GUID. */
+ *p = g;
+ } else
smp->status |= IB_SMP_INVALID_FIELD;
return reply(smp);
diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c
index b36f6fb3e37..a0673c1eef7 100644
--- a/drivers/infiniband/hw/ipath/ipath_mr.c
+++ b/drivers/infiniband/hw/ipath/ipath_mr.c
@@ -138,6 +138,7 @@ struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd,
goto bail;
}
+ mr->mr.pd = pd;
mr->mr.user_base = *iova_start;
mr->mr.iova = *iova_start;
mr->mr.length = 0;
@@ -197,6 +198,7 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region,
goto bail;
}
+ mr->mr.pd = pd;
mr->mr.user_base = region->user_base;
mr->mr.iova = region->virt_base;
mr->mr.length = region->length;
@@ -289,6 +291,7 @@ struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags,
* Resources are allocated but no valid mapping (RKEY can't be
* used).
*/
+ fmr->mr.pd = pd;
fmr->mr.user_base = 0;
fmr->mr.iova = 0;
fmr->mr.length = 0;
diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c
index 224b0f40767..46c1c89bf6a 100644
--- a/drivers/infiniband/hw/ipath/ipath_qp.c
+++ b/drivers/infiniband/hw/ipath/ipath_qp.c
@@ -335,6 +335,7 @@ static void ipath_reset_qp(struct ipath_qp *qp)
qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
qp->r_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
qp->r_nak_state = 0;
+ qp->r_wrid_valid = 0;
qp->s_rnr_timeout = 0;
qp->s_head = 0;
qp->s_tail = 0;
@@ -342,6 +343,7 @@ static void ipath_reset_qp(struct ipath_qp *qp)
qp->s_last = 0;
qp->s_ssn = 1;
qp->s_lsn = 0;
+ qp->s_wait_credit = 0;
if (qp->r_rq.wq) {
qp->r_rq.wq->head = 0;
qp->r_rq.wq->tail = 0;
@@ -352,12 +354,13 @@ static void ipath_reset_qp(struct ipath_qp *qp)
/**
* ipath_error_qp - put a QP into an error state
* @qp: the QP to put into an error state
+ * @err: the receive completion error to signal if a RWQE is active
*
* Flushes both send and receive work queues.
* QP s_lock should be held and interrupts disabled.
*/
-void ipath_error_qp(struct ipath_qp *qp)
+void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err)
{
struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
struct ib_wc wc;
@@ -373,7 +376,6 @@ void ipath_error_qp(struct ipath_qp *qp)
list_del_init(&qp->piowait);
spin_unlock(&dev->pending_lock);
- wc.status = IB_WC_WR_FLUSH_ERR;
wc.vendor_err = 0;
wc.byte_len = 0;
wc.imm_data = 0;
@@ -385,6 +387,12 @@ void ipath_error_qp(struct ipath_qp *qp)
wc.sl = 0;
wc.dlid_path_bits = 0;
wc.port_num = 0;
+ if (qp->r_wrid_valid) {
+ qp->r_wrid_valid = 0;
+ wc.status = err;
+ ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1);
+ }
+ wc.status = IB_WC_WR_FLUSH_ERR;
while (qp->s_last != qp->s_head) {
struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
@@ -501,7 +509,7 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
break;
case IB_QPS_ERR:
- ipath_error_qp(qp);
+ ipath_error_qp(qp, IB_WC_GENERAL_ERR);
break;
default:
@@ -516,7 +524,7 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
qp->remote_qpn = attr->dest_qp_num;
if (attr_mask & IB_QP_SQ_PSN) {
- qp->s_next_psn = attr->sq_psn;
+ qp->s_psn = qp->s_next_psn = attr->sq_psn;
qp->s_last_psn = qp->s_next_psn - 1;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c
index a08654042c0..a504cf67f27 100644
--- a/drivers/infiniband/hw/ipath/ipath_rc.c
+++ b/drivers/infiniband/hw/ipath/ipath_rc.c
@@ -201,6 +201,18 @@ int ipath_make_rc_req(struct ipath_qp *qp,
qp->s_rnr_timeout)
goto done;
+ /* Limit the number of packets sent without an ACK. */
+ if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) {
+ qp->s_wait_credit = 1;
+ dev->n_rc_stalls++;
+ spin_lock(&dev->pending_lock);
+ if (list_empty(&qp->timerwait))
+ list_add_tail(&qp->timerwait,
+ &dev->pending[dev->pending_index]);
+ spin_unlock(&dev->pending_lock);
+ goto done;
+ }
+
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
hwords = 5;
bth0 = 0;
@@ -221,7 +233,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* Check if send work queue is empty. */
if (qp->s_tail == qp->s_head)
goto done;
- qp->s_psn = wqe->psn = qp->s_next_psn;
+ wqe->psn = qp->s_next_psn;
newreq = 1;
}
/*
@@ -393,12 +405,6 @@ int ipath_make_rc_req(struct ipath_qp *qp,
ss = &qp->s_sge;
len = qp->s_len;
if (len > pmtu) {
- /*
- * Request an ACK every 1/2 MB to avoid retransmit
- * timeouts.
- */
- if (((wqe->length - len) % (512 * 1024)) == 0)
- bth2 |= 1 << 31;
len = pmtu;
break;
}
@@ -435,12 +441,6 @@ int ipath_make_rc_req(struct ipath_qp *qp,
ss = &qp->s_sge;
len = qp->s_len;
if (len > pmtu) {
- /*
- * Request an ACK every 1/2 MB to avoid retransmit
- * timeouts.
- */
- if (((wqe->length - len) % (512 * 1024)) == 0)
- bth2 |= 1 << 31;
len = pmtu;
break;
}
@@ -498,6 +498,8 @@ int ipath_make_rc_req(struct ipath_qp *qp,
*/
goto done;
}
+ if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0)
+ bth2 |= 1 << 31; /* Request ACK. */
qp->s_len -= len;
qp->s_hdrwords = hwords;
qp->s_cur_sge = ss;
@@ -737,6 +739,15 @@ bail:
return;
}
+static inline void update_last_psn(struct ipath_qp *qp, u32 psn)
+{
+ if (qp->s_wait_credit) {
+ qp->s_wait_credit = 0;
+ tasklet_hi_schedule(&qp->s_task);
+ }
+ qp->s_last_psn = psn;
+}
+
/**
* do_rc_ack - process an incoming RC ACK
* @qp: the QP the ACK came in on
@@ -805,7 +816,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
* The last valid PSN seen is the previous
* request's.
*/
- qp->s_last_psn = wqe->psn - 1;
+ update_last_psn(qp, wqe->psn - 1);
/* Retry this request. */
ipath_restart_rc(qp, wqe->psn, &wc);
/*
@@ -864,7 +875,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
ipath_get_credit(qp, aeth);
qp->s_rnr_retry = qp->s_rnr_retry_cnt;
qp->s_retry = qp->s_retry_cnt;
- qp->s_last_psn = psn;
+ update_last_psn(qp, psn);
ret = 1;
goto bail;
@@ -883,7 +894,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
goto bail;
/* The last valid PSN is the previous PSN. */
- qp->s_last_psn = psn - 1;
+ update_last_psn(qp, psn - 1);
dev->n_rc_resends += (int)qp->s_psn - (int)psn;
@@ -898,7 +909,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
case 3: /* NAK */
/* The last valid PSN seen is the previous request's. */
if (qp->s_last != qp->s_tail)
- qp->s_last_psn = wqe->psn - 1;
+ update_last_psn(qp, wqe->psn - 1);
switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) &
IPATH_AETH_CREDIT_MASK) {
case 0: /* PSN sequence error */
@@ -1071,7 +1082,7 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
* since we don't want s_sge modified.
*/
qp->s_len -= pmtu;
- qp->s_last_psn = psn;
+ update_last_psn(qp, psn);
spin_unlock_irqrestore(&qp->s_lock, flags);
ipath_copy_sge(&qp->s_sge, data, pmtu);
goto bail;
@@ -1223,7 +1234,7 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
* Address range must be a subset of the original
* request and start on pmtu boundaries.
*/
- ok = ipath_rkey_ok(dev, &qp->s_rdma_sge,
+ ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
qp->s_rdma_len, vaddr, rkey,
IB_ACCESS_REMOTE_READ);
if (unlikely(!ok)) {
@@ -1282,6 +1293,14 @@ done:
return 1;
}
+static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err)
+{
+ spin_lock_irq(&qp->s_lock);
+ qp->state = IB_QPS_ERR;
+ ipath_error_qp(qp, err);
+ spin_unlock_irq(&qp->s_lock);
+}
+
/**
* ipath_rc_rcv - process an incoming RC packet
* @dev: the device this packet came in on
@@ -1309,6 +1328,10 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
struct ib_reth *reth;
int header_in_data;
+ /* Validate the SLID. See Ch. 9.6.1.5 */
+ if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid))
+ goto done;
+
/* Check for GRH */
if (!has_grh) {
ohdr = &hdr->u.oth;
@@ -1370,8 +1393,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
*/
if (qp->r_ack_state >= OP(COMPARE_SWAP))
goto send_ack;
- /* XXX Flush WQEs */
- qp->state = IB_QPS_ERR;
+ ipath_rc_error(qp, IB_WC_REM_INV_REQ_ERR);
qp->r_ack_state = OP(SEND_ONLY);
qp->r_nak_state = IB_NAK_INVALID_REQUEST;
qp->r_ack_psn = qp->r_psn;
@@ -1477,9 +1499,9 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
goto nack_inv;
ipath_copy_sge(&qp->r_sge, data, tlen);
qp->r_msn++;
- if (opcode == OP(RDMA_WRITE_LAST) ||
- opcode == OP(RDMA_WRITE_ONLY))
+ if (!qp->r_wrid_valid)
break;
+ qp->r_wrid_valid = 0;
wc.wr_id = qp->r_wr_id;
wc.status = IB_WC_SUCCESS;
wc.opcode = IB_WC_RECV;
@@ -1517,7 +1539,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
int ok;
/* Check rkey & NAK */
- ok = ipath_rkey_ok(dev, &qp->r_sge,
+ ok = ipath_rkey_ok(qp, &qp->r_sge,
qp->r_len, vaddr, rkey,
IB_ACCESS_REMOTE_WRITE);
if (unlikely(!ok))
@@ -1559,7 +1581,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
int ok;
/* Check rkey & NAK */
- ok = ipath_rkey_ok(dev, &qp->s_rdma_sge,
+ ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
qp->s_rdma_len, vaddr, rkey,
IB_ACCESS_REMOTE_READ);
if (unlikely(!ok)) {
@@ -1618,7 +1640,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
goto nack_inv;
rkey = be32_to_cpu(ateth->rkey);
/* Check rkey & NAK */
- if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge,
+ if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge,
sizeof(u64), vaddr, rkey,
IB_ACCESS_REMOTE_ATOMIC)))
goto nack_acc;
@@ -1670,8 +1692,7 @@ nack_acc:
* is pending though.
*/
if (qp->r_ack_state < OP(COMPARE_SWAP)) {
- /* XXX Flush WQEs */
- qp->state = IB_QPS_ERR;
+ ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR);
qp->r_ack_state = OP(RDMA_WRITE_ONLY);
qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
qp->r_ack_psn = qp->r_psn;
diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h
index 6e23b3d632b..dffc76016d3 100644
--- a/drivers/infiniband/hw/ipath/ipath_registers.h
+++ b/drivers/infiniband/hw/ipath/ipath_registers.h
@@ -134,10 +134,24 @@
#define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40
#define INFINIPATH_HWE_RXEMEMPARITYERR_MASK 0x7FULL
#define INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT 44
-#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL
-#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL
#define INFINIPATH_HWE_IBCBUSTOSPCPARITYERR 0x4000000000000000ULL
#define INFINIPATH_HWE_IBCBUSFRSPCPARITYERR 0x8000000000000000ULL
+/* txe mem parity errors (shift by INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) */
+#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF 0x1ULL
+#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC 0x2ULL
+#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOLAUNCHFIFO 0x4ULL
+/* rxe mem parity errors (shift by INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) */
+#define INFINIPATH_HWE_RXEMEMPARITYERR_RCVBUF 0x01ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_LOOKUPQ 0x02ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x04ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x08ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF 0x10ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO 0x40ULL
+/* waldo specific -- find the rest in ipath_6110.c */
+#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL
+/* monty specific -- find the rest in ipath_6120.c */
+#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL
/* kr_hwdiagctrl bits */
#define INFINIPATH_DC_FORCETXEMEMPARITYERR_MASK 0xFULL
@@ -209,9 +223,9 @@
/* combination link status states that we use with some frequency */
#define IPATH_IBSTATE_MASK ((INFINIPATH_IBCS_LINKTRAININGSTATE_MASK \
- << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
+ << INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) | \
(INFINIPATH_IBCS_LINKSTATE_MASK \
- <<INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT))
+ <<INFINIPATH_IBCS_LINKSTATE_SHIFT))
#define IPATH_IBSTATE_INIT ((INFINIPATH_IBCS_L_STATE_INIT \
<< INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
(INFINIPATH_IBCS_LT_STATE_LINKUP \
@@ -302,6 +316,17 @@
typedef u64 ipath_err_t;
+/* The following change with the type of device, so
+ * need to be part of the ipath_devdata struct, or
+ * we could have problems plugging in devices of
+ * different types (e.g. one HT, one PCIE)
+ * in one system, to be managed by one driver.
+ * On the other hand, this file is may also be included
+ * by other code, so leave the declarations here
+ * temporarily. Minor footprint issue if common-model
+ * linker used, none if C89+ linker used.
+ */
+
/* mask of defined bits for various registers */
extern u64 infinipath_i_bitsextant;
extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
@@ -310,13 +335,6 @@ extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
extern u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;
/*
- * register bits for selecting i2c direction and values, used for I2C serial
- * flash
- */
-extern u16 ipath_gpio_sda_num, ipath_gpio_scl_num;
-extern u64 ipath_gpio_sda, ipath_gpio_scl;
-
-/*
* These are the infinipath general register numbers (not offsets).
* The kernel registers are used directly, those beyond the kernel
* registers are calculated from one of the base registers. The use of
diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c
index 5c1da2d25e0..f7530512045 100644
--- a/drivers/infiniband/hw/ipath/ipath_ruc.c
+++ b/drivers/infiniband/hw/ipath/ipath_ruc.c
@@ -108,7 +108,6 @@ void ipath_insert_rnr_queue(struct ipath_qp *qp)
static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe)
{
- struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
int user = to_ipd(qp->ibqp.pd)->user;
int i, j, ret;
struct ib_wc wc;
@@ -119,8 +118,7 @@ static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe)
continue;
/* Check LKEY */
if ((user && wqe->sg_list[i].lkey == 0) ||
- !ipath_lkey_ok(&dev->lk_table,
- &qp->r_sg_list[j], &wqe->sg_list[i],
+ !ipath_lkey_ok(qp, &qp->r_sg_list[j], &wqe->sg_list[i],
IB_ACCESS_LOCAL_WRITE))
goto bad_lkey;
qp->r_len += wqe->sg_list[i].length;
@@ -231,6 +229,7 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only)
}
}
spin_unlock_irqrestore(&rq->lock, flags);
+ qp->r_wrid_valid = 1;
bail:
return ret;
@@ -326,7 +325,7 @@ again:
case IB_WR_RDMA_WRITE:
if (wqe->length == 0)
break;
- if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, wqe->length,
+ if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, wqe->length,
wqe->wr.wr.rdma.remote_addr,
wqe->wr.wr.rdma.rkey,
IB_ACCESS_REMOTE_WRITE))) {
@@ -350,7 +349,7 @@ again:
break;
case IB_WR_RDMA_READ:
- if (unlikely(!ipath_rkey_ok(dev, &sqp->s_sge, wqe->length,
+ if (unlikely(!ipath_rkey_ok(qp, &sqp->s_sge, wqe->length,
wqe->wr.wr.rdma.remote_addr,
wqe->wr.wr.rdma.rkey,
IB_ACCESS_REMOTE_READ)))
@@ -365,7 +364,7 @@ again:
case IB_WR_ATOMIC_CMP_AND_SWP:
case IB_WR_ATOMIC_FETCH_AND_ADD:
- if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, sizeof(u64),
+ if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64),
wqe->wr.wr.rdma.remote_addr,
wqe->wr.wr.rdma.rkey,
IB_ACCESS_REMOTE_ATOMIC)))
@@ -575,8 +574,7 @@ int ipath_post_ruc_send(struct ipath_qp *qp, struct ib_send_wr *wr)
}
if (wr->sg_list[i].length == 0)
continue;
- if (!ipath_lkey_ok(&to_idev(qp->ibqp.device)->lk_table,
- &wqe->sg_list[j], &wr->sg_list[i],
+ if (!ipath_lkey_ok(qp, &wqe->sg_list[j], &wr->sg_list[i],
acc)) {
spin_unlock_irqrestore(&qp->s_lock, flags);
ret = -EINVAL;
diff --git a/drivers/infiniband/hw/ipath/ipath_srq.c b/drivers/infiniband/hw/ipath/ipath_srq.c
index 941e866d951..94033503400 100644
--- a/drivers/infiniband/hw/ipath/ipath_srq.c
+++ b/drivers/infiniband/hw/ipath/ipath_srq.c
@@ -104,11 +104,6 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
u32 sz;
struct ib_srq *ret;
- if (dev->n_srqs_allocated == ib_ipath_max_srqs) {
- ret = ERR_PTR(-ENOMEM);
- goto done;
- }
-
if (srq_init_attr->attr.max_wr == 0) {
ret = ERR_PTR(-EINVAL);
goto done;
@@ -180,10 +175,17 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
spin_lock_init(&srq->rq.lock);
srq->rq.wq->head = 0;
srq->rq.wq->tail = 0;
- srq->rq.max_sge = srq_init_attr->attr.max_sge;
srq->limit = srq_init_attr->attr.srq_limit;
- dev->n_srqs_allocated++;
+ spin_lock(&dev->n_srqs_lock);
+ if (dev->n_srqs_allocated == ib_ipath_max_srqs) {
+ spin_unlock(&dev->n_srqs_lock);
+ ret = ERR_PTR(-ENOMEM);
+ goto bail_wq;
+ }
+
+ dev->n_srqs_allocated++;
+ spin_unlock(&dev->n_srqs_lock);
ret = &srq->ibsrq;
goto done;
@@ -351,8 +353,13 @@ int ipath_destroy_srq(struct ib_srq *ibsrq)
struct ipath_srq *srq = to_isrq(ibsrq);
struct ipath_ibdev *dev = to_idev(ibsrq->device);
+ spin_lock(&dev->n_srqs_lock);
dev->n_srqs_allocated--;
- vfree(srq->rq.wq);
+ spin_unlock(&dev->n_srqs_lock);
+ if (srq->ip)
+ kref_put(&srq->ip->ref, ipath_release_mmap_info);
+ else
+ vfree(srq->rq.wq);
kfree(srq);
return 0;
diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c
index e299148c4b6..182de34f9f4 100644
--- a/drivers/infiniband/hw/ipath/ipath_sysfs.c
+++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c
@@ -257,7 +257,7 @@ static ssize_t store_guid(struct device *dev,
struct ipath_devdata *dd = dev_get_drvdata(dev);
ssize_t ret;
unsigned short guid[8];
- __be64 nguid;
+ __be64 new_guid;
u8 *ng;
int i;
@@ -266,7 +266,7 @@ static ssize_t store_guid(struct device *dev,
&guid[4], &guid[5], &guid[6], &guid[7]) != 8)
goto invalid;
- ng = (u8 *) &nguid;
+ ng = (u8 *) &new_guid;
for (i = 0; i < 8; i++) {
if (guid[i] > 0xff)
@@ -274,7 +274,10 @@ static ssize_t store_guid(struct device *dev,
ng[i] = guid[i];
}
- dd->ipath_guid = nguid;
+ if (new_guid == 0)
+ goto invalid;
+
+ dd->ipath_guid = new_guid;
dd->ipath_nguid = 1;
ret = strlen(buf);
@@ -297,6 +300,16 @@ static ssize_t show_nguid(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_nguid);
}
+static ssize_t show_nports(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+ /* Return the number of user ports available. */
+ return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_cfgports - 1);
+}
+
static ssize_t show_serial(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -608,6 +621,7 @@ static DEVICE_ATTR(mlid, S_IWUSR | S_IRUGO, show_mlid, store_mlid);
static DEVICE_ATTR(mtu, S_IWUSR | S_IRUGO, show_mtu, store_mtu);
static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, show_enabled, store_enabled);
static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL);
+static DEVICE_ATTR(nports, S_IRUGO, show_nports, NULL);
static DEVICE_ATTR(reset, S_IWUSR, NULL, store_reset);
static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL);
static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
@@ -623,6 +637,7 @@ static struct attribute *dev_attributes[] = {
&dev_attr_mlid.attr,
&dev_attr_mtu.attr,
&dev_attr_nguid.attr,
+ &dev_attr_nports.attr,
&dev_attr_serial.attr,
&dev_attr_status.attr,
&dev_attr_status_str.attr,
diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c
index 0fd3cded16b..e636cfd67a8 100644
--- a/drivers/infiniband/hw/ipath/ipath_uc.c
+++ b/drivers/infiniband/hw/ipath/ipath_uc.c
@@ -246,6 +246,10 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
struct ib_reth *reth;
int header_in_data;
+ /* Validate the SLID. See Ch. 9.6.1.5 */
+ if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid))
+ goto done;
+
/* Check for GRH */
if (!has_grh) {
ohdr = &hdr->u.oth;
@@ -440,7 +444,7 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
int ok;
/* Check rkey */
- ok = ipath_rkey_ok(dev, &qp->r_sge, qp->r_len,
+ ok = ipath_rkey_ok(qp, &qp->r_sge, qp->r_len,
vaddr, rkey,
IB_ACCESS_REMOTE_WRITE);
if (unlikely(!ok)) {
diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c
index 6991d1d74e3..49f1102af8b 100644
--- a/drivers/infiniband/hw/ipath/ipath_ud.c
+++ b/drivers/infiniband/hw/ipath/ipath_ud.c
@@ -39,7 +39,6 @@
static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe,
u32 *lengthp, struct ipath_sge_state *ss)
{
- struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
int user = to_ipd(qp->ibqp.pd)->user;
int i, j, ret;
struct ib_wc wc;
@@ -50,8 +49,7 @@ static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe,
continue;
/* Check LKEY */
if ((user && wqe->sg_list[i].lkey == 0) ||
- !ipath_lkey_ok(&dev->lk_table,
- j ? &ss->sg_list[j - 1] : &ss->sge,
+ !ipath_lkey_ok(qp, j ? &ss->sg_list[j - 1] : &ss->sge,
&wqe->sg_list[i], IB_ACCESS_LOCAL_WRITE))
goto bad_lkey;
*lengthp += wqe->sg_list[i].length;
@@ -343,7 +341,7 @@ int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr)
if (wr->sg_list[i].length == 0)
continue;
- if (!ipath_lkey_ok(&dev->lk_table, ss.num_sge ?
+ if (!ipath_lkey_ok(qp, ss.num_sge ?
sg_list + ss.num_sge - 1 : &ss.sge,
&wr->sg_list[i], 0)) {
ret = -EINVAL;
diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c
index e32fca9faf8..413754b1d8a 100644
--- a/drivers/infiniband/hw/ipath/ipath_user_pages.c
+++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c
@@ -90,6 +90,62 @@ bail:
}
/**
+ * ipath_map_page - a safety wrapper around pci_map_page()
+ *
+ * A dma_addr of all 0's is interpreted by the chip as "disabled".
+ * Unfortunately, it can also be a valid dma_addr returned on some
+ * architectures.
+ *
+ * The powerpc iommu assigns dma_addrs in ascending order, so we don't
+ * have to bother with retries or mapping a dummy page to insure we
+ * don't just get the same mapping again.
+ *
+ * I'm sure we won't be so lucky with other iommu's, so FIXME.
+ */
+dma_addr_t ipath_map_page(struct pci_dev *hwdev, struct page *page,
+ unsigned long offset, size_t size, int direction)
+{
+ dma_addr_t phys;
+
+ phys = pci_map_page(hwdev, page, offset, size, direction);
+
+ if (phys == 0) {
+ pci_unmap_page(hwdev, phys, size, direction);
+ phys = pci_map_page(hwdev, page, offset, size, direction);
+ /*
+ * FIXME: If we get 0 again, we should keep this page,
+ * map another, then free the 0 page.
+ */
+ }
+
+ return phys;
+}
+
+/**
+ * ipath_map_single - a safety wrapper around pci_map_single()
+ *
+ * Same idea as ipath_map_page().
+ */
+dma_addr_t ipath_map_single(struct pci_dev *hwdev, void *ptr, size_t size,
+ int direction)
+{
+ dma_addr_t phys;
+
+ phys = pci_map_single(hwdev, ptr, size, direction);
+
+ if (phys == 0) {
+ pci_unmap_single(hwdev, phys, size, direction);
+ phys = pci_map_single(hwdev, ptr, size, direction);
+ /*
+ * FIXME: If we get 0 again, we should keep this page,
+ * map another, then free the 0 page.
+ */
+ }
+
+ return phys;
+}
+
+/**
* ipath_get_user_pages - lock user pages into memory
* @start_page: the start page
* @num_pages: the number of pages
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index b8381c5e72b..42eaed88c28 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -898,7 +898,8 @@ int ipath_get_counters(struct ipath_devdata *dd,
ipath_snap_cntr(dd, dd->ipath_cregs->cr_erricrccnt) +
ipath_snap_cntr(dd, dd->ipath_cregs->cr_errvcrccnt) +
ipath_snap_cntr(dd, dd->ipath_cregs->cr_errlpcrccnt) +
- ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt);
+ ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt) +
+ dd->ipath_rxfc_unsupvl_errs;
cntrs->port_rcv_remphys_errors =
ipath_snap_cntr(dd, dd->ipath_cregs->cr_rcvebpcnt);
cntrs->port_xmit_discards =
@@ -911,8 +912,10 @@ int ipath_get_counters(struct ipath_devdata *dd,
ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt);
cntrs->port_rcv_packets =
ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt);
- cntrs->local_link_integrity_errors = dd->ipath_lli_errors;
- cntrs->excessive_buffer_overrun_errors = 0; /* XXX */
+ cntrs->local_link_integrity_errors =
+ (dd->ipath_flags & IPATH_GPIO_ERRINTRS) ?
+ dd->ipath_lli_errs : dd->ipath_lli_errors;
+ cntrs->excessive_buffer_overrun_errors = dd->ipath_overrun_thresh_errs;
ret = 0;
@@ -1199,6 +1202,7 @@ static struct ib_ah *ipath_create_ah(struct ib_pd *pd,
struct ipath_ah *ah;
struct ib_ah *ret;
struct ipath_ibdev *dev = to_idev(pd->device);
+ unsigned long flags;
/* A multicast address requires a GRH (see ch. 8.4.1). */
if (ah_attr->dlid >= IPATH_MULTICAST_LID_BASE &&
@@ -1225,16 +1229,16 @@ static struct ib_ah *ipath_create_ah(struct ib_pd *pd,
goto bail;
}
- spin_lock(&dev->n_ahs_lock);
+ spin_lock_irqsave(&dev->n_ahs_lock, flags);
if (dev->n_ahs_allocated == ib_ipath_max_ahs) {
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
kfree(ah);
ret = ERR_PTR(-ENOMEM);
goto bail;
}
dev->n_ahs_allocated++;
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
/* ib_create_ah() will initialize ah->ibah. */
ah->attr = *ah_attr;
@@ -1255,10 +1259,11 @@ static int ipath_destroy_ah(struct ib_ah *ibah)
{
struct ipath_ibdev *dev = to_idev(ibah->device);
struct ipath_ah *ah = to_iah(ibah);
+ unsigned long flags;
- spin_lock(&dev->n_ahs_lock);
+ spin_lock_irqsave(&dev->n_ahs_lock, flags);
dev->n_ahs_allocated--;
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
kfree(ah);
@@ -1380,11 +1385,13 @@ static int enable_timer(struct ipath_devdata *dd)
* processing.
*/
if (dd->ipath_flags & IPATH_GPIO_INTR) {
+ u64 val;
ipath_write_kreg(dd, dd->ipath_kregs->kr_debugportselect,
0x2074076542310ULL);
/* Enable GPIO bit 2 interrupt */
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask,
- (u64) (1 << 2));
+ val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask);
+ val |= (u64) (1 << IPATH_GPIO_PORT0_BIT);
+ ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val);
}
init_timer(&dd->verbs_timer);
@@ -1399,8 +1406,17 @@ static int enable_timer(struct ipath_devdata *dd)
static int disable_timer(struct ipath_devdata *dd)
{
/* Disable GPIO bit 2 interrupt */
- if (dd->ipath_flags & IPATH_GPIO_INTR)
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask, 0);
+ if (dd->ipath_flags & IPATH_GPIO_INTR) {
+ u64 val;
+ /* Disable GPIO bit 2 interrupt */
+ val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask);
+ val &= ~((u64) (1 << IPATH_GPIO_PORT0_BIT));
+ ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val);
+ /*
+ * We might want to undo changes to debugportselect,
+ * but how?
+ */
+ }
del_timer_sync(&dd->verbs_timer);
@@ -1683,6 +1699,7 @@ static ssize_t show_stats(struct class_device *cdev, char *buf)
"RC OTH NAKs %d\n"
"RC timeouts %d\n"
"RC RDMA dup %d\n"
+ "RC stalls %d\n"
"piobuf wait %d\n"
"no piobuf %d\n"
"PKT drops %d\n"
@@ -1690,7 +1707,7 @@ static ssize_t show_stats(struct class_device *cdev, char *buf)
dev->n_rc_resends, dev->n_rc_qacks, dev->n_rc_acks,
dev->n_seq_naks, dev->n_rdma_seq, dev->n_rnr_naks,
dev->n_other_naks, dev->n_timeouts,
- dev->n_rdma_dup_busy, dev->n_piowait,
+ dev->n_rdma_dup_busy, dev->n_rc_stalls, dev->n_piowait,
dev->n_no_piobuf, dev->n_pkt_drops, dev->n_wqe_errs);
for (i = 0; i < ARRAY_SIZE(dev->opstats); i++) {
const struct ipath_opcode_stats *si = &dev->opstats[i];
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
index 09bbb3f9a21..8039f6e5f0c 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -220,6 +220,7 @@ struct ipath_segarray {
};
struct ipath_mregion {
+ struct ib_pd *pd; /* shares refcnt of ibmr.pd */
u64 user_base; /* User's address for this region */
u64 iova; /* IB start address of this region */
size_t length;
@@ -364,12 +365,14 @@ struct ipath_qp {
u8 r_min_rnr_timer; /* retry timeout value for RNR NAKs */
u8 r_reuse_sge; /* for UC receive errors */
u8 r_sge_inx; /* current index into sg_list */
+ u8 r_wrid_valid; /* r_wrid set but CQ entry not yet made */
u8 qp_access_flags;
u8 s_max_sge; /* size of s_wq->sg_list */
u8 s_retry_cnt; /* number of times to retry */
u8 s_rnr_retry_cnt;
u8 s_retry; /* requester retry counter */
u8 s_rnr_retry; /* requester RNR retry counter */
+ u8 s_wait_credit; /* limit number of unacked packets sent */
u8 s_pkey_index; /* PKEY index to use */
u8 timeout; /* Timeout for this QP */
enum ib_mtu path_mtu;
@@ -393,6 +396,8 @@ struct ipath_qp {
#define IPATH_S_BUSY 0
#define IPATH_S_SIGNAL_REQ_WR 1
+#define IPATH_PSN_CREDIT 2048
+
/*
* Since struct ipath_swqe is not a fixed size, we can't simply index into
* struct ipath_qp.s_wq. This function does the array index computation.
@@ -521,6 +526,7 @@ struct ipath_ibdev {
u32 n_rnr_naks;
u32 n_other_naks;
u32 n_timeouts;
+ u32 n_rc_stalls;
u32 n_pkt_drops;
u32 n_vl15_dropped;
u32 n_wqe_errs;
@@ -634,6 +640,8 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
int ipath_destroy_qp(struct ib_qp *ibqp);
+void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err);
+
int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata);
@@ -653,12 +661,6 @@ int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords,
void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
-int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
- u32 len, u64 vaddr, u32 rkey, int acc);
-
-int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
- struct ib_sge *sge, int acc);
-
void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length);
void ipath_skip_sge(struct ipath_sge_state *ss, u32 length);
@@ -683,10 +685,10 @@ int ipath_alloc_lkey(struct ipath_lkey_table *rkt,
void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey);
-int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
+int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
struct ib_sge *sge, int acc);
-int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
+int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
u32 len, u64 vaddr, u32 rkey, int acc);
int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
diff --git a/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c b/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
index 036fde662aa..0095bb70f34 100644
--- a/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
+++ b/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
@@ -38,13 +38,23 @@
#include "ipath_kernel.h"
/**
- * ipath_unordered_wc - indicate whether write combining is ordered
+ * ipath_enable_wc - enable write combining for MMIO writes to the device
+ * @dd: infinipath device
*
- * PowerPC systems (at least those in the 970 processor family)
- * write partially filled store buffers in address order, but will write
- * completely filled store buffers in "random" order, and therefore must
- * have serialization for correctness with current InfiniPath chips.
+ * Nothing to do on PowerPC, so just return without error.
+ */
+int ipath_enable_wc(struct ipath_devdata *dd)
+{
+ return 0;
+}
+
+/**
+ * ipath_unordered_wc - indicate whether write combining is unordered
*
+ * Because our performance depends on our ability to do write
+ * combining mmio writes in the most efficient way, we need to
+ * know if we are on a processor that may reorder stores when
+ * write combining.
*/
int ipath_unordered_wc(void)
{
diff --git a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
index f8f9e2e8cbd..04696e62da8 100644
--- a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
+++ b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
@@ -123,6 +123,8 @@ int ipath_enable_wc(struct ipath_devdata *dd)
ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, "
"cookie is %d\n", cookie);
dd->ipath_wc_cookie = cookie;
+ dd->ipath_wc_base = (unsigned long) pioaddr;
+ dd->ipath_wc_len = (unsigned long) piolen;
}
}
@@ -136,9 +138,16 @@ int ipath_enable_wc(struct ipath_devdata *dd)
void ipath_disable_wc(struct ipath_devdata *dd)
{
if (dd->ipath_wc_cookie) {
+ int r;
ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n");
- mtrr_del(dd->ipath_wc_cookie, 0, 0);
- dd->ipath_wc_cookie = 0;
+ r = mtrr_del(dd->ipath_wc_cookie, dd->ipath_wc_base,
+ dd->ipath_wc_len);
+ if (r < 0)
+ dev_info(&dd->pcidev->dev,
+ "mtrr_del(%lx, %lx, %lx) failed: %d\n",
+ dd->ipath_wc_cookie, dd->ipath_wc_base,
+ dd->ipath_wc_len, r);
+ dd->ipath_wc_cookie = 0; /* even on failure */
}
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 5dde380e8db..f1cb83688b3 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -141,7 +141,7 @@ static int ipoib_mcg_open(struct inode *inode, struct file *file)
return ret;
seq = file->private_data;
- seq->private = inode->u.generic_ip;
+ seq->private = inode->i_private;
return 0;
}
@@ -247,7 +247,7 @@ static int ipoib_path_open(struct inode *inode, struct file *file)
return ret;
seq = file->private_data;
- seq->private = inode->u.generic_ip;
+ seq->private = inode->i_private;
return 0;
}
diff --git a/drivers/infiniband/ulp/iser/Kconfig b/drivers/infiniband/ulp/iser/Kconfig
index 365a1b5f19e..aecbb9083f0 100644
--- a/drivers/infiniband/ulp/iser/Kconfig
+++ b/drivers/infiniband/ulp/iser/Kconfig
@@ -1,11 +1,12 @@
config INFINIBAND_ISER
- tristate "ISCSI RDMA Protocol"
+ tristate "iSCSI Extensions for RDMA (iSER)"
depends on INFINIBAND && SCSI && INET
select SCSI_ISCSI_ATTRS
---help---
- Support for the ISCSI RDMA Protocol over InfiniBand. This
- allows you to access storage devices that speak ISER/ISCSI
- over InfiniBand.
+ Support for the iSCSI Extensions for RDMA (iSER) Protocol
+ over InfiniBand. This allows you to access storage devices
+ that speak iSCSI over iSER over InfiniBand.
- The ISER protocol is defined by IETF.
- See <http://www.ietf.org/>.
+ The iSER protocol is defined by IETF.
+ See <http://www.ietf.org/internet-drafts/draft-ietf-ips-iser-05.txt>
+ and <http://www.infinibandta.org/members/spec/iser_annex_060418.pdf>
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 2a14fe2e322..eb6f98d8228 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -317,6 +317,8 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
struct iscsi_iser_conn *iser_conn = conn->dd_data;
iscsi_conn_teardown(cls_conn);
+ if (iser_conn->ib_conn)
+ iser_conn->ib_conn->iser_conn = NULL;
kfree(iser_conn);
}
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 2cf9ae0def1..9c53916f28c 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -192,7 +192,7 @@ struct iser_regd_buf {
struct iser_dto {
struct iscsi_iser_cmd_task *ctask;
- struct iscsi_iser_conn *conn;
+ struct iser_conn *ib_conn;
int notify_enable;
/* vector of registered buffers */
@@ -355,4 +355,11 @@ int iser_post_send(struct iser_desc *tx_desc);
int iser_conn_state_comp(struct iser_conn *ib_conn,
enum iser_ib_conn_state comp);
+
+int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
+ struct iser_data_buf *data,
+ enum iser_data_dir iser_dir,
+ enum dma_data_direction dma_dir);
+
+void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask);
#endif
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index ccf56f6f723..9b3d79c796c 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -66,42 +66,6 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto,
dto->regd_vector_len++;
}
-static int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
- struct iser_data_buf *data,
- enum iser_data_dir iser_dir,
- enum dma_data_direction dma_dir)
-{
- struct device *dma_device;
-
- iser_ctask->dir[iser_dir] = 1;
- dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
-
- data->dma_nents = dma_map_sg(dma_device, data->buf, data->size, dma_dir);
- if (data->dma_nents == 0) {
- iser_err("dma_map_sg failed!!!\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
-{
- struct device *dma_device;
- struct iser_data_buf *data;
-
- dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
-
- if (iser_ctask->dir[ISER_DIR_IN]) {
- data = &iser_ctask->data[ISER_DIR_IN];
- dma_unmap_sg(dma_device, data->buf, data->size, DMA_FROM_DEVICE);
- }
-
- if (iser_ctask->dir[ISER_DIR_OUT]) {
- data = &iser_ctask->data[ISER_DIR_OUT];
- dma_unmap_sg(dma_device, data->buf, data->size, DMA_TO_DEVICE);
- }
-}
-
/* Register user buffer memory and initialize passive rdma
* dto descriptor. Total data size is stored in
* iser_ctask->data[ISER_DIR_IN].data_len
@@ -249,7 +213,7 @@ static int iser_post_receive_control(struct iscsi_conn *conn)
}
recv_dto = &rx_desc->dto;
- recv_dto->conn = iser_conn;
+ recv_dto->ib_conn = iser_conn->ib_conn;
recv_dto->regd_vector_len = 0;
regd_hdr = &rx_desc->hdr_regd_buf;
@@ -296,7 +260,7 @@ static void iser_create_send_desc(struct iscsi_iser_conn *iser_conn,
regd_hdr->virt_addr = tx_desc; /* == &tx_desc->iser_header */
regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN;
- send_dto->conn = iser_conn;
+ send_dto->ib_conn = iser_conn->ib_conn;
send_dto->notify_enable = 1;
send_dto->regd_vector_len = 0;
@@ -588,7 +552,7 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
unsigned long dto_xfer_len)
{
struct iser_dto *dto = &rx_desc->dto;
- struct iscsi_iser_conn *conn = dto->conn;
+ struct iscsi_iser_conn *conn = dto->ib_conn->iser_conn;
struct iscsi_session *session = conn->iscsi_conn->session;
struct iscsi_cmd_task *ctask;
struct iscsi_iser_cmd_task *iser_ctask;
@@ -641,7 +605,8 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
void iser_snd_completion(struct iser_desc *tx_desc)
{
struct iser_dto *dto = &tx_desc->dto;
- struct iscsi_iser_conn *iser_conn = dto->conn;
+ struct iser_conn *ib_conn = dto->ib_conn;
+ struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
struct iscsi_conn *conn = iser_conn->iscsi_conn;
struct iscsi_mgmt_task *mtask;
@@ -652,7 +617,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
if (tx_desc->type == ISCSI_TX_DATAOUT)
kmem_cache_free(ig.desc_cache, tx_desc);
- atomic_dec(&iser_conn->ib_conn->post_send_buf_count);
+ atomic_dec(&ib_conn->post_send_buf_count);
write_lock(conn->recv_lock);
if (conn->suspend_tx) {
@@ -698,14 +663,19 @@ void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
{
int deferred;
+ int is_rdma_aligned = 1;
/* if we were reading, copy back to unaligned sglist,
* anyway dma_unmap and free the copy
*/
- if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL)
+ if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) {
+ is_rdma_aligned = 0;
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
- if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL)
+ }
+ if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
+ is_rdma_aligned = 0;
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
+ }
if (iser_ctask->dir[ISER_DIR_IN]) {
deferred = iser_regd_buff_release
@@ -725,7 +695,9 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
}
}
- iser_dma_unmap_task_data(iser_ctask);
+ /* if the data was unaligned, it was already unmapped and then copied */
+ if (is_rdma_aligned)
+ iser_dma_unmap_task_data(iser_ctask);
}
void iser_dto_buffs_release(struct iser_dto *dto)
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index d0b03f42658..0606744c3f8 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -369,6 +369,44 @@ static void iser_page_vec_build(struct iser_data_buf *data,
}
}
+int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
+ struct iser_data_buf *data,
+ enum iser_data_dir iser_dir,
+ enum dma_data_direction dma_dir)
+{
+ struct device *dma_device;
+
+ iser_ctask->dir[iser_dir] = 1;
+ dma_device =
+ iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+
+ data->dma_nents = dma_map_sg(dma_device, data->buf, data->size, dma_dir);
+ if (data->dma_nents == 0) {
+ iser_err("dma_map_sg failed!!!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
+{
+ struct device *dma_device;
+ struct iser_data_buf *data;
+
+ dma_device =
+ iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+
+ if (iser_ctask->dir[ISER_DIR_IN]) {
+ data = &iser_ctask->data[ISER_DIR_IN];
+ dma_unmap_sg(dma_device, data->buf, data->size, DMA_FROM_DEVICE);
+ }
+
+ if (iser_ctask->dir[ISER_DIR_OUT]) {
+ data = &iser_ctask->data[ISER_DIR_OUT];
+ dma_unmap_sg(dma_device, data->buf, data->size, DMA_TO_DEVICE);
+ }
+}
+
/**
* iser_reg_rdma_mem - Registers memory intended for RDMA,
* obtaining rkey and va
@@ -394,6 +432,10 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
iser_err("rdma alignment violation %d/%d aligned\n",
aligned_len, mem->size);
iser_data_buf_dump(mem);
+
+ /* unmap the command data before accessing it */
+ iser_dma_unmap_task_data(iser_ctask);
+
/* allocate copy buf, if we are writing, copy the */
/* unaligned scatterlist, dma map the copy */
if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0)
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index ecdca7fc1e4..18a00003499 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -571,6 +571,8 @@ void iser_conn_release(struct iser_conn *ib_conn)
/* on EVENT_ADDR_ERROR there's no device yet for this conn */
if (device != NULL)
iser_device_try_release(device);
+ if (ib_conn->iser_conn)
+ ib_conn->iser_conn->ib_conn = NULL;
kfree(ib_conn);
}
@@ -694,7 +696,7 @@ int iser_post_recv(struct iser_desc *rx_desc)
struct iser_dto *recv_dto = &rx_desc->dto;
/* Retrieve conn */
- ib_conn = recv_dto->conn->ib_conn;
+ ib_conn = recv_dto->ib_conn;
iser_dto_to_iov(recv_dto, iov, 2);
@@ -727,7 +729,7 @@ int iser_post_send(struct iser_desc *tx_desc)
struct iser_conn *ib_conn;
struct iser_dto *dto = &tx_desc->dto;
- ib_conn = dto->conn->ib_conn;
+ ib_conn = dto->ib_conn;
iser_dto_to_iov(dto, iov, MAX_REGD_BUF_VECTOR_LEN);
@@ -774,7 +776,7 @@ static void iser_comp_error_worker(void *data)
static void iser_handle_comp_error(struct iser_desc *desc)
{
struct iser_dto *dto = &desc->dto;
- struct iser_conn *ib_conn = dto->conn->ib_conn;
+ struct iser_conn *ib_conn = dto->ib_conn;
iser_dto_buffs_release(dto);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a9dda56f62c..83eac3a66bc 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -183,4 +183,13 @@ config KEYBOARD_HIL
This driver implements support for HIL-keyboards attached
to your machine, so normally you should say Y here.
+config KEYBOARD_OMAP
+ tristate "TI OMAP keypad support"
+ depends on (ARCH_OMAP1 || ARCH_OMAP2)
+ help
+ Say Y here if you want to use the OMAP keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap-keypad.
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 2708167ba17..b265391f1f1 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -15,4 +15,5 @@ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
new file mode 100644
index 00000000000..d436287d1d2
--- /dev/null
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -0,0 +1,492 @@
+/*
+ * linux/drivers/input/keyboard/omap-keypad.c
+ *
+ * OMAP Keypad Driver
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@nokia.com>
+ *
+ * Added support for H2 & H3 Keypad
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/keypad.h>
+#include <asm/arch/menelaus.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/arch/mux.h>
+
+#undef NEW_BOARD_LEARNING_MODE
+
+static void omap_kp_tasklet(unsigned long);
+static void omap_kp_timer(unsigned long);
+
+static unsigned char keypad_state[8];
+static DEFINE_MUTEX(kp_enable_mutex);
+static int kp_enable = 1;
+static int kp_cur_group = -1;
+
+struct omap_kp {
+ struct input_dev *input;
+ struct timer_list timer;
+ int irq;
+ unsigned int rows;
+ unsigned int cols;
+ unsigned long delay;
+ unsigned int debounce;
+};
+
+DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
+
+static int *keymap;
+static unsigned int *row_gpios;
+static unsigned int *col_gpios;
+
+#ifdef CONFIG_ARCH_OMAP2
+static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value)
+{
+ int col;
+ for (col = 0; col < omap_kp->cols; col++) {
+ if (value & (1 << col))
+ omap_set_gpio_dataout(col_gpios[col], 1);
+ else
+ omap_set_gpio_dataout(col_gpios[col], 0);
+ }
+}
+
+static u8 get_row_gpio_val(struct omap_kp *omap_kp)
+{
+ int row;
+ u8 value = 0;
+
+ for (row = 0; row < omap_kp->rows; row++) {
+ if (omap_get_gpio_datain(row_gpios[row]))
+ value |= (1 << row);
+ }
+ return value;
+}
+#else
+#define set_col_gpio_val(x, y) do {} while (0)
+#define get_row_gpio_val(x) 0
+#endif
+
+static irqreturn_t omap_kp_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct omap_kp *omap_kp = dev_id;
+
+ /* disable keyboard interrupt and schedule for handling */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->rows; i++)
+ disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ } else
+ /* disable keyboard interrupt and schedule for handling */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ tasklet_schedule(&kp_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static void omap_kp_timer(unsigned long data)
+{
+ tasklet_schedule(&kp_tasklet);
+}
+
+static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
+{
+ int col = 0;
+
+ /* read the keypad status */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->rows; i++)
+ disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+
+ /* read the keypad status */
+ for (col = 0; col < omap_kp->cols; col++) {
+ set_col_gpio_val(omap_kp, ~(1 << col));
+ state[col] = ~(get_row_gpio_val(omap_kp)) & 0x3f;
+ }
+ set_col_gpio_val(omap_kp, 0);
+
+ } else {
+ /* disable keyboard interrupt and schedule for handling */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ /* read the keypad status */
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ for (col = 0; col < omap_kp->cols; col++) {
+ omap_writew(~(1 << col) & 0xff,
+ OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+
+ udelay(omap_kp->delay);
+
+ state[col] = ~omap_readw(OMAP_MPUIO_BASE +
+ OMAP_MPUIO_KBR_LATCH) & 0xff;
+ }
+ omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ udelay(2);
+ }
+}
+
+static inline int omap_kp_find_key(int col, int row)
+{
+ int i, key;
+
+ key = KEY(col, row, 0);
+ for (i = 0; keymap[i] != 0; i++)
+ if ((keymap[i] & 0xff000000) == key)
+ return keymap[i] & 0x00ffffff;
+ return -1;
+}
+
+static void omap_kp_tasklet(unsigned long data)
+{
+ struct omap_kp *omap_kp_data = (struct omap_kp *) data;
+ unsigned char new_state[8], changed, key_down = 0;
+ int col, row;
+ int spurious = 0;
+
+ /* check for any changes */
+ omap_kp_scan_keypad(omap_kp_data, new_state);
+
+ /* check for changes and print those */
+ for (col = 0; col < omap_kp_data->cols; col++) {
+ changed = new_state[col] ^ keypad_state[col];
+ key_down |= new_state[col];
+ if (changed == 0)
+ continue;
+
+ for (row = 0; row < omap_kp_data->rows; row++) {
+ int key;
+ if (!(changed & (1 << row)))
+ continue;
+#ifdef NEW_BOARD_LEARNING_MODE
+ printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col,
+ row, (new_state[col] & (1 << row)) ?
+ "pressed" : "released");
+#else
+ key = omap_kp_find_key(col, row);
+ if (key < 0) {
+ printk(KERN_WARNING
+ "omap-keypad: Spurious key event %d-%d\n",
+ col, row);
+ /* We scan again after a couple of seconds */
+ spurious = 1;
+ continue;
+ }
+
+ if (!(kp_cur_group == (key & GROUP_MASK) ||
+ kp_cur_group == -1))
+ continue;
+
+ kp_cur_group = key & GROUP_MASK;
+ input_report_key(omap_kp_data->input, key & ~GROUP_MASK,
+ new_state[col] & (1 << row));
+#endif
+ }
+ }
+ memcpy(keypad_state, new_state, sizeof(keypad_state));
+
+ if (key_down) {
+ int delay = HZ / 20;
+ /* some key is pressed - keep irq disabled and use timer
+ * to poll the keypad */
+ if (spurious)
+ delay = 2 * HZ;
+ mod_timer(&omap_kp_data->timer, jiffies + delay);
+ } else {
+ /* enable interrupts */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp_data->rows; i++)
+ enable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ } else {
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ kp_cur_group = -1;
+ }
+ }
+}
+
+static ssize_t omap_kp_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", kp_enable);
+}
+
+static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state;
+
+ if (sscanf(buf, "%u", &state) != 1)
+ return -EINVAL;
+
+ if ((state != 1) && (state != 0))
+ return -EINVAL;
+
+ mutex_lock(&kp_enable_mutex);
+ if (state != kp_enable) {
+ if (state)
+ enable_irq(INT_KEYBOARD);
+ else
+ disable_irq(INT_KEYBOARD);
+ kp_enable = state;
+ }
+ mutex_unlock(&kp_enable_mutex);
+
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);
+
+#ifdef CONFIG_PM
+static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+
+static int omap_kp_resume(struct platform_device *dev)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+#else
+#define omap_kp_suspend NULL
+#define omap_kp_resume NULL
+#endif
+
+static int __init omap_kp_probe(struct platform_device *pdev)
+{
+ struct omap_kp *omap_kp;
+ struct input_dev *input_dev;
+ struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
+ int i, col_idx, row_idx, irq_idx, ret;
+
+ if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+ printk(KERN_ERR "No rows, cols or keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!omap_kp || !input_dev) {
+ kfree(omap_kp);
+ input_free_device(input_dev);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, omap_kp);
+
+ omap_kp->input = input_dev;
+
+ /* Disable the interrupt for the MPUIO keyboard */
+ if (!cpu_is_omap24xx())
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ keymap = pdata->keymap;
+
+ if (pdata->rep)
+ set_bit(EV_REP, input_dev->evbit);
+
+ if (pdata->delay)
+ omap_kp->delay = pdata->delay;
+
+ if (pdata->row_gpios && pdata->col_gpios) {
+ row_gpios = pdata->row_gpios;
+ col_gpios = pdata->col_gpios;
+ }
+
+ omap_kp->rows = pdata->rows;
+ omap_kp->cols = pdata->cols;
+
+ if (cpu_is_omap24xx()) {
+ /* Cols: outputs */
+ for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) {
+ if (omap_request_gpio(col_gpios[col_idx]) < 0) {
+ printk(KERN_ERR "Failed to request"
+ "GPIO%d for keypad\n",
+ col_gpios[col_idx]);
+ goto err1;
+ }
+ omap_set_gpio_direction(col_gpios[col_idx], 0);
+ }
+ /* Rows: inputs */
+ for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) {
+ if (omap_request_gpio(row_gpios[row_idx]) < 0) {
+ printk(KERN_ERR "Failed to request"
+ "GPIO%d for keypad\n",
+ row_gpios[row_idx]);
+ goto err2;
+ }
+ omap_set_gpio_direction(row_gpios[row_idx], 1);
+ }
+ }
+
+ setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
+
+ /* get the irq and init timer*/
+ tasklet_enable(&kp_tasklet);
+ kp_tasklet.data = (unsigned long) omap_kp;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_enable);
+ if (ret < 0)
+ goto err2;
+
+ /* setup input device */
+ set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; keymap[i] != 0; i++)
+ set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
+ input_dev->name = "omap-keypad";
+ input_dev->phys = "omap-keypad/input0";
+ input_dev->cdev.dev = &pdev->dev;
+ input_dev->private = omap_kp;
+
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ input_dev->keycode = keymap;
+ input_dev->keycodesize = sizeof(unsigned int);
+ input_dev->keycodemax = pdata->keymapsize;
+
+ ret = input_register_device(omap_kp->input);
+ if (ret < 0) {
+ printk(KERN_ERR "Unable to register omap-keypad input device\n");
+ goto err3;
+ }
+
+ if (pdata->dbounce)
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
+
+ /* scan current status and enable interrupt */
+ omap_kp_scan_keypad(omap_kp, keypad_state);
+ if (!cpu_is_omap24xx()) {
+ omap_kp->irq = platform_get_irq(pdev, 0);
+ if (omap_kp->irq >= 0) {
+ if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,
+ "omap-keypad", omap_kp) < 0)
+ goto err4;
+ }
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ } else {
+ for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
+ if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
+ omap_kp_interrupt,
+ IRQF_TRIGGER_FALLING,
+ "omap-keypad", omap_kp) < 0)
+ goto err5;
+ }
+ }
+ return 0;
+err5:
+ for (i = irq_idx-1; i >=0; i--)
+ free_irq(row_gpios[i], 0);
+err4:
+ input_unregister_device(omap_kp->input);
+ input_dev = NULL;
+err3:
+ device_remove_file(&pdev->dev, &dev_attr_enable);
+err2:
+ for (i = row_idx-1; i >=0; i--)
+ omap_free_gpio(row_gpios[i]);
+err1:
+ for (i = col_idx-1; i >=0; i--)
+ omap_free_gpio(col_gpios[i]);
+
+ kfree(omap_kp);
+ input_free_device(input_dev);
+
+ return -EINVAL;
+}
+
+static int omap_kp_remove(struct platform_device *pdev)
+{
+ struct omap_kp *omap_kp = platform_get_drvdata(pdev);
+
+ /* disable keypad interrupt handling */
+ tasklet_disable(&kp_tasklet);
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->cols; i++)
+ omap_free_gpio(col_gpios[i]);
+ for (i = 0; i < omap_kp->rows; i++) {
+ omap_free_gpio(row_gpios[i]);
+ free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
+ }
+ } else {
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ free_irq(omap_kp->irq, 0);
+ }
+
+ del_timer_sync(&omap_kp->timer);
+ tasklet_kill(&kp_tasklet);
+
+ /* unregister everything */
+ input_unregister_device(omap_kp->input);
+
+ kfree(omap_kp);
+
+ return 0;
+}
+
+static struct platform_driver omap_kp_driver = {
+ .probe = omap_kp_probe,
+ .remove = omap_kp_remove,
+ .suspend = omap_kp_suspend,
+ .resume = omap_kp_resume,
+ .driver = {
+ .name = "omap-keypad",
+ },
+};
+
+static int __devinit omap_kp_init(void)
+{
+ printk(KERN_INFO "OMAP Keypad Driver\n");
+ return platform_driver_register(&omap_kp_driver);
+}
+
+static void __exit omap_kp_exit(void)
+{
+ platform_driver_unregister(&omap_kp_driver);
+}
+
+module_init(omap_kp_init);
+module_exit(omap_kp_exit);
+
+MODULE_AUTHOR("Timo Teräs");
+MODULE_DESCRIPTION("OMAP Keypad Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
index fa97e0f79e7..ee6c2f40cdf 100644
--- a/drivers/input/touchscreen/hp680_ts_input.c
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -15,7 +15,6 @@
#define HP680_TS_ABS_Y_MIN 80
#define HP680_TS_ABS_Y_MAX 910
-#define SCPCR 0xa4000116
#define PHDR 0xa400012e
#define SCPDR 0xa4000136
@@ -77,19 +76,6 @@ static irqreturn_t hp680_ts_interrupt(int irq, void *dev, struct pt_regs *regs)
static int __init hp680_ts_init(void)
{
- u8 scpdr;
- u16 scpcr;
-
- scpdr = ctrl_inb(SCPDR);
- scpdr |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y;
- scpdr &= ~SCPDR_TS_SCAN_ENABLE;
- ctrl_outb(scpdr, SCPDR);
-
- scpcr = ctrl_inw(SCPCR);
- scpcr &= ~SCPCR_TS_MASK;
- scpcr |= SCPCR_TS_ENABLE;
- ctrl_outw(scpcr, SCPCR);
-
hp680_ts_dev = input_allocate_device();
if (!hp680_ts_dev)
return -ENOMEM;
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
index 9ea6bd0ddc3..2dd1b57b0ba 100644
--- a/drivers/isdn/capi/capifs.c
+++ b/drivers/isdn/capi/capifs.c
@@ -104,7 +104,6 @@ capifs_fill_super(struct super_block *s, void *data, int silent)
inode->i_ino = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_blocks = 0;
- inode->i_blksize = 1024;
inode->i_uid = inode->i_gid = 0;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
inode->i_op = &simple_dir_inode_operations;
@@ -149,7 +148,6 @@ void capifs_new_ncci(unsigned int number, dev_t device)
if (!inode)
return;
inode->i_ino = number+2;
- inode->i_blksize = 1024;
inode->i_uid = config.setuid ? config.uid : current->fsuid;
inode->i_gid = config.setgid ? config.gid : current->fsgid;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 3845defd490..5cfbe6a3801 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -192,7 +192,7 @@ static char *get_usb_statmsg(int status)
return "bit stuffing error, timeout, or unknown USB error";
case -EILSEQ:
return "CRC mismatch, timeout, or unknown USB error";
- case -ETIMEDOUT:
+ case -ETIME:
return "timed out";
case -EPIPE:
return "endpoint stalled";
diff --git a/drivers/isdn/hisax/hfc_usb.h b/drivers/isdn/hisax/hfc_usb.h
index ec52c1a7c22..6349367ed48 100644
--- a/drivers/isdn/hisax/hfc_usb.h
+++ b/drivers/isdn/hisax/hfc_usb.h
@@ -137,11 +137,11 @@ static struct hfcusb_symbolic_list urb_errlist[] = {
{-ENXIO, "URB already queued"},
{-EFBIG, "Too much ISO frames requested"},
{-ENOSR, "Buffer error (overrun)"},
- {-EPIPE, "Specified endpoint is stalled (device not responding)"},
+ {-EPIPE, "Specified endpoint is stalled"},
{-EOVERFLOW, "Babble (bad cable?)"},
{-EPROTO, "Bit-stuff error (bad cable?)"},
- {-EILSEQ, "CRC/Timeout"},
- {-ETIMEDOUT, "NAK (device does not respond)"},
+ {-EILSEQ, "CRC or missing token"},
+ {-ETIME, "Device did not respond"},
{-ESHUTDOWN, "Device unplugged"},
{-1, NULL}
};
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 43da8ae1b2a..1f8d6ae66b4 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -1614,8 +1614,8 @@ isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
struct sk_buff *skb;
unsigned char *p;
struct in_device *in_dev = NULL;
- u32 addr = 0; /* local ipv4 address */
- u32 mask = 0; /* local netmask */
+ __be32 addr = 0; /* local ipv4 address */
+ __be32 mask = 0; /* local netmask */
if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
/* take primary(first) address of interface */
diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c
index 713c4a8aa77..45ba3d45bcb 100644
--- a/drivers/leds/leds-net48xx.c
+++ b/drivers/leds/leds-net48xx.c
@@ -16,6 +16,7 @@
#include <linux/leds.h>
#include <linux/err.h>
#include <asm/io.h>
+#include <linux/nsc_gpio.h>
#include <linux/scx200_gpio.h>
#define DRVNAME "net48xx-led"
@@ -26,10 +27,7 @@ static struct platform_device *pdev;
static void net48xx_error_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- if (value)
- scx200_gpio_set_high(NET48XX_ERROR_LED_GPIO);
- else
- scx200_gpio_set_low(NET48XX_ERROR_LED_GPIO);
+ scx200_gpio_ops.gpio_set(NET48XX_ERROR_LED_GPIO, value ? 1 : 0);
}
static struct led_classdev net48xx_error_led = {
@@ -81,7 +79,8 @@ static int __init net48xx_led_init(void)
{
int ret;
- if (!scx200_gpio_present()) {
+ /* small hack, but scx200_gpio doesn't set .dev if the probe fails */
+ if (!scx200_gpio_ops.dev) {
ret = -ENODEV;
goto out;
}
diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c
index a82f313d9dc..6c29fe727c0 100644
--- a/drivers/macintosh/via-pmu-backlight.c
+++ b/drivers/macintosh/via-pmu-backlight.c
@@ -16,7 +16,7 @@
#define MAX_PMU_LEVEL 0xFF
static struct backlight_properties pmu_backlight_data;
-static spinlock_t pmu_backlight_lock;
+static DEFINE_SPINLOCK(pmu_backlight_lock);
static int sleeping;
static u8 bl_curve[FB_BACKLIGHT_LEVELS];
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index aceb61d9fbc..83f79de7174 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -397,12 +397,7 @@ static int wf_sat_detach(struct i2c_client *client)
static int __init sat_sensors_init(void)
{
- int err;
-
- err = i2c_add_driver(&wf_sat_driver);
- if (err < 0)
- return err;
- return 0;
+ return i2c_add_driver(&wf_sat_driver);
}
static void __exit sat_sensors_exit(void)
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
index 001c71b6be6..410fa6d620f 100644
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -981,7 +981,7 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
- if (state.event > PM_EVENT_ON) {
+ if (1) {
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
cinergyt2_suspend_rc(cinergyt2);
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
index 9002f35aa95..88b283731bb 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
@@ -80,7 +80,6 @@ static void dvb_usb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
switch (urb->status) {
case 0: /* success */
- case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index 02651998f8d..de077a75719 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -215,7 +215,7 @@ static void ttusb_dec_handle_irq( struct urb *urb, struct pt_regs *regs)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is dead, cleanup */
dprintk("%s:urb shutting down with status: %d\n",
__FUNCTION__, urb->status);
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 03ee170181f..70de6c96e20 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -111,7 +111,6 @@ static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = {
.getsda = bttv_bit_getsda,
.getscl = bttv_bit_getscl,
.udelay = 16,
- .mdelay = 10,
.timeout = 200,
};
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index a74ed632dda..27b5dbb2ca1 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -163,7 +163,6 @@ static struct i2c_algo_bit_data cx8800_i2c_algo_template = {
.getsda = cx8800_bit_getsda,
.getscl = cx8800_bit_getscl,
.udelay = 16,
- .mdelay = 10,
.timeout = 200,
};
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c
index 751a754a45e..2b4f1970c7d 100644
--- a/drivers/media/video/cx88/cx88-vp3054-i2c.c
+++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c
@@ -100,7 +100,6 @@ static struct i2c_algo_bit_data vp3054_i2c_algo_template = {
.getsda = vp3054_bit_getsda,
.getscl = vp3054_bit_getscl,
.udelay = 16,
- .mdelay = 10,
.timeout = 200,
};
diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c
index 1b07a61c2eb..5d8cd283fcd 100644
--- a/drivers/media/video/ov511.c
+++ b/drivers/media/video/ov511.c
@@ -301,10 +301,11 @@ static struct symbolic_list senlist[] = {
static struct symbolic_list urb_errlist[] = {
{ -ENOSR, "Buffer error (overrun)" },
{ -EPIPE, "Stalled (device not responding)" },
- { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EOVERFLOW, "Babble (device sends too much data)" },
{ -EPROTO, "Bit-stuff error (bad cable?)" },
- { -EILSEQ, "CRC/Timeout" },
- { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -EILSEQ, "CRC/Timeout (bad cable?)" },
+ { -ETIME, "Device does not respond to token" },
+ { -ETIMEDOUT, "Device does not respond to command" },
{ -1, NULL }
};
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index d4703944df9..53c4b5790d5 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -711,7 +711,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
- case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break;
+ case -ETIME: errmsg = "Device does not respond"; break;
}
PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
/* Give up after a number of contiguous errors on the USB bus.
diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c
index 20f211b55ad..2912326a5ae 100644
--- a/drivers/media/video/w9968cf.c
+++ b/drivers/media/video/w9968cf.c
@@ -586,15 +586,14 @@ static struct w9968cf_symbolic_list urb_errlist[] = {
{ -EFBIG, "Too much ISO frames requested" },
{ -ENOSR, "Buffer error (overrun)" },
{ -EPIPE, "Specified endpoint is stalled (device not responding)"},
- { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EOVERFLOW, "Babble (too much data)" },
{ -EPROTO, "Bit-stuff error (bad cable?)" },
{ -EILSEQ, "CRC/Timeout" },
- { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -ETIME, "Device does not respond to token" },
+ { -ETIMEDOUT, "Device does not respond to command" },
{ -1, NULL }
};
-
-
/****************************************************************************
* Memory management functions *
****************************************************************************/
diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c
index aa80ddd317b..9f21d0ba0f0 100644
--- a/drivers/media/video/zoran_card.c
+++ b/drivers/media/video/zoran_card.c
@@ -820,7 +820,6 @@ static struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
.getsda = zoran_i2c_getsda,
.getscl = zoran_i2c_getscl,
.udelay = 10,
- .mdelay = 0,
.timeout = 100,
};
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c
index 02776814443..82938ad6ddb 100644
--- a/drivers/mfd/ucb1x00-ts.c
+++ b/drivers/mfd/ucb1x00-ts.c
@@ -58,6 +58,7 @@ static int adcsync;
static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
{
struct input_dev *idev = ts->idev;
+
input_report_abs(idev, ABS_X, x);
input_report_abs(idev, ABS_Y, y);
input_report_abs(idev, ABS_PRESSURE, pressure);
@@ -67,6 +68,7 @@ static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x
static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
{
struct input_dev *idev = ts->idev;
+
input_report_abs(idev, ABS_PRESSURE, 0);
input_sync(idev);
}
@@ -189,6 +191,7 @@ static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts)
{
unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
+
if (machine_is_collie())
return (!(val & (UCB_TS_CR_TSPX_LOW)));
else
@@ -291,6 +294,7 @@ static int ucb1x00_thread(void *_ts)
static void ucb1x00_ts_irq(int idx, void *id)
{
struct ucb1x00_ts *ts = id;
+
ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
wake_up(&ts->irq_wait);
}
@@ -372,36 +376,43 @@ static int ucb1x00_ts_resume(struct ucb1x00_dev *dev)
static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
{
struct ucb1x00_ts *ts;
+ struct input_dev *idev;
+ int err;
ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
- if (!ts)
- return -ENOMEM;
-
- ts->idev = input_allocate_device();
- if (!ts->idev) {
- kfree(ts);
- return -ENOMEM;
+ idev = input_allocate_device();
+ if (!ts || !idev) {
+ err = -ENOMEM;
+ goto fail;
}
ts->ucb = dev->ucb;
+ ts->idev = idev;
ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
- ts->idev->private = ts;
- ts->idev->name = "Touchscreen panel";
- ts->idev->id.product = ts->ucb->id;
- ts->idev->open = ucb1x00_ts_open;
- ts->idev->close = ucb1x00_ts_close;
+ idev->private = ts;
+ idev->name = "Touchscreen panel";
+ idev->id.product = ts->ucb->id;
+ idev->open = ucb1x00_ts_open;
+ idev->close = ucb1x00_ts_close;
- __set_bit(EV_ABS, ts->idev->evbit);
- __set_bit(ABS_X, ts->idev->absbit);
- __set_bit(ABS_Y, ts->idev->absbit);
- __set_bit(ABS_PRESSURE, ts->idev->absbit);
+ __set_bit(EV_ABS, idev->evbit);
+ __set_bit(ABS_X, idev->absbit);
+ __set_bit(ABS_Y, idev->absbit);
+ __set_bit(ABS_PRESSURE, idev->absbit);
- input_register_device(ts->idev);
+ err = input_register_device(idev);
+ if (err)
+ goto fail;
dev->priv = ts;
return 0;
+
+ fail:
+ input_free_device(idev);
+ kfree(ts);
+ return err;
}
static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c
index 4a35caff5d0..b99dc507de2 100644
--- a/drivers/misc/ibmasm/ibmasmfs.c
+++ b/drivers/misc/ibmasm/ibmasmfs.c
@@ -147,7 +147,6 @@ static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
if (ret) {
ret->i_mode = mode;
ret->i_uid = ret->i_gid = 0;
- ret->i_blksize = PAGE_CACHE_SIZE;
ret->i_blocks = 0;
ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
}
@@ -175,7 +174,7 @@ static struct dentry *ibmasmfs_create_file (struct super_block *sb,
}
inode->i_fop = fops;
- inode->u.generic_ip = data;
+ inode->i_private = data;
d_add(dentry, inode);
return dentry;
@@ -244,7 +243,7 @@ static int command_file_open(struct inode *inode, struct file *file)
{
struct ibmasmfs_command_data *command_data;
- if (!inode->u.generic_ip)
+ if (!inode->i_private)
return -ENODEV;
command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
@@ -252,7 +251,7 @@ static int command_file_open(struct inode *inode, struct file *file)
return -ENOMEM;
command_data->command = NULL;
- command_data->sp = inode->u.generic_ip;
+ command_data->sp = inode->i_private;
file->private_data = command_data;
return 0;
}
@@ -351,10 +350,10 @@ static int event_file_open(struct inode *inode, struct file *file)
struct ibmasmfs_event_data *event_data;
struct service_processor *sp;
- if (!inode->u.generic_ip)
+ if (!inode->i_private)
return -ENODEV;
- sp = inode->u.generic_ip;
+ sp = inode->i_private;
event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
if (!event_data)
@@ -439,14 +438,14 @@ static int r_heartbeat_file_open(struct inode *inode, struct file *file)
{
struct ibmasmfs_heartbeat_data *rhbeat;
- if (!inode->u.generic_ip)
+ if (!inode->i_private)
return -ENODEV;
rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
if (!rhbeat)
return -ENOMEM;
- rhbeat->sp = (struct service_processor *)inode->u.generic_ip;
+ rhbeat->sp = inode->i_private;
rhbeat->active = 0;
ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
file->private_data = rhbeat;
@@ -508,7 +507,7 @@ static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf,
static int remote_settings_file_open(struct inode *inode, struct file *file)
{
- file->private_data = inode->u.generic_ip;
+ file->private_data = inode->i_private;
return 0;
}
diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c
index 6b7638b8429..cb142a66098 100644
--- a/drivers/mmc/at91_mci.c
+++ b/drivers/mmc/at91_mci.c
@@ -822,6 +822,7 @@ static int at91_mci_probe(struct platform_device *pdev)
mmc->f_min = 375000;
mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_BYTEBLOCK;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -850,7 +851,7 @@ static int at91_mci_probe(struct platform_device *pdev)
/*
* Allocate the MCI interrupt
*/
- ret = request_irq(AT91_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
+ ret = request_irq(AT91RM9200_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
if (ret) {
printk(KERN_ERR "Failed to request MCI interrupt\n");
clk_disable(mci_clk);
@@ -906,7 +907,7 @@ static int at91_mci_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
at91_mci_disable();
- free_irq(AT91_ID_MCI, host);
+ free_irq(AT91RM9200_ID_MCI, host);
mmc_free_host(mmc);
clk_disable(mci_clk); /* Disable the peripheral clock */
diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c
index fb606165af3..61268da1395 100644
--- a/drivers/mmc/au1xmmc.c
+++ b/drivers/mmc/au1xmmc.c
@@ -731,7 +731,7 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
}
}
-static void au1xmmc_dma_callback(int irq, void *dev_id, struct pt_regs *regs)
+static void au1xmmc_dma_callback(int irq, void *dev_id)
{
struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id;
diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c
index fb6565b98f3..1b79dd271aa 100644
--- a/drivers/mmc/imxmmc.c
+++ b/drivers/mmc/imxmmc.c
@@ -956,7 +956,7 @@ static int imxmci_probe(struct platform_device *pdev)
mmc->f_min = 150000;
mmc->f_max = CLK_RATE/2;
mmc->ocr_avail = MMC_VDD_32_33;
- mmc->caps |= MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK;
/* MMC core transfer sizes tunable parameters */
mmc->max_hw_segs = 64;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 74eaaee66de..5b9caa7978d 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -996,7 +996,6 @@ static void mmc_read_scrs(struct mmc_host *host)
mmc_set_data_timeout(&data, card, 0);
- data.blksz_bits = 3;
data.blksz = 1 << 3;
data.blocks = 1;
data.flags = MMC_DATA_READ;
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index a0e0dad1b41..db0e8ad439a 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -32,6 +32,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
+#include <linux/mmc/host.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -165,6 +166,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
do {
struct mmc_blk_request brq;
struct mmc_command cmd;
+ u32 readcmd, writecmd;
memset(&brq, 0, sizeof(struct mmc_blk_request));
brq.mrq.cmd = &brq.cmd;
@@ -172,7 +174,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.cmd.arg = req->sector << 9;
brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
- brq.data.blksz_bits = md->block_bits;
brq.data.blksz = 1 << md->block_bits;
brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
brq.stop.opcode = MMC_STOP_TRANSMISSION;
@@ -181,20 +182,31 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ);
- if (rq_data_dir(req) == READ) {
- brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
- brq.data.flags |= MMC_DATA_READ;
- } else {
- brq.cmd.opcode = MMC_WRITE_BLOCK;
- brq.data.flags |= MMC_DATA_WRITE;
+ /*
+ * If the host doesn't support multiple block writes, force
+ * block writes to single block.
+ */
+ if (rq_data_dir(req) != READ &&
+ !(card->host->caps & MMC_CAP_MULTIWRITE))
brq.data.blocks = 1;
- }
if (brq.data.blocks > 1) {
brq.data.flags |= MMC_DATA_MULTI;
brq.mrq.stop = &brq.stop;
+ readcmd = MMC_READ_MULTIPLE_BLOCK;
+ writecmd = MMC_WRITE_MULTIPLE_BLOCK;
} else {
brq.mrq.stop = NULL;
+ readcmd = MMC_READ_SINGLE_BLOCK;
+ writecmd = MMC_WRITE_BLOCK;
+ }
+
+ if (rq_data_dir(req) == READ) {
+ brq.cmd.opcode = readcmd;
+ brq.data.flags |= MMC_DATA_READ;
+ } else {
+ brq.cmd.opcode = writecmd;
+ brq.data.flags |= MMC_DATA_WRITE;
}
brq.data.sg = mq->sg;
@@ -219,27 +231,29 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
goto cmd_err;
}
- do {
- int err;
-
- cmd.opcode = MMC_SEND_STATUS;
- cmd.arg = card->rca << 16;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 5);
- if (err) {
- printk(KERN_ERR "%s: error %d requesting status\n",
- req->rq_disk->disk_name, err);
- goto cmd_err;
- }
- } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+ if (rq_data_dir(req) != READ) {
+ do {
+ int err;
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 5);
+ if (err) {
+ printk(KERN_ERR "%s: error %d requesting status\n",
+ req->rq_disk->disk_name, err);
+ goto cmd_err;
+ }
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
#if 0
- if (cmd.resp[0] & ~0x00000900)
- printk(KERN_ERR "%s: status = %08x\n",
- req->rq_disk->disk_name, cmd.resp[0]);
- if (mmc_decode_status(cmd.resp))
- goto cmd_err;
+ if (cmd.resp[0] & ~0x00000900)
+ printk(KERN_ERR "%s: status = %08x\n",
+ req->rq_disk->disk_name, cmd.resp[0]);
+ if (mmc_decode_status(cmd.resp))
+ goto cmd_err;
#endif
+ }
/*
* A block was successfully transferred.
diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
index 1886562abdd..2b5a0cc9ea5 100644
--- a/drivers/mmc/mmci.c
+++ b/drivers/mmc/mmci.c
@@ -69,12 +69,13 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
unsigned int datactrl, timeout, irqmask;
unsigned long long clks;
void __iomem *base;
+ int blksz_bits;
DBG(host, "blksz %04x blks %04x flags %08x\n",
- 1 << data->blksz_bits, data->blocks, data->flags);
+ data->blksz, data->blocks, data->flags);
host->data = data;
- host->size = data->blocks << data->blksz_bits;
+ host->size = data->blksz;
host->data_xfered = 0;
mmci_init_sg(host, data);
@@ -88,7 +89,10 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
writel(timeout, base + MMCIDATATIMER);
writel(host->size, base + MMCIDATALENGTH);
- datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4;
+ blksz_bits = ffs(data->blksz) - 1;
+ BUG_ON(1 << blksz_bits != data->blksz);
+
+ datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
if (data->flags & MMC_DATA_READ) {
datactrl |= MCI_DPSM_DIRECTION;
irqmask = MCI_RXFIFOHALFFULLMASK;
@@ -145,7 +149,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
unsigned int status)
{
if (status & MCI_DATABLOCKEND) {
- host->data_xfered += 1 << data->blksz_bits;
+ host->data_xfered += data->blksz;
}
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
if (status & MCI_DATACRCFAIL)
@@ -505,6 +509,7 @@ static int mmci_probe(struct amba_device *dev, void *id)
mmc->f_min = (host->mclk + 511) / 512;
mmc->f_max = min(host->mclk, fmax);
mmc->ocr_avail = plat->ocr_mask;
+ mmc->caps = MMC_CAP_MULTIWRITE;
/*
* We can do SGIO
diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c
index ddf06b32c15..52c9e52e6b7 100644
--- a/drivers/mmc/omap.c
+++ b/drivers/mmc/omap.c
@@ -1034,13 +1034,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
host->irq = pdev->resource[1].start;
host->base = (void __iomem*)IO_ADDRESS(r->start);
- if (minfo->wire4)
- mmc->caps |= MMC_CAP_4_BIT_DATA;
-
mmc->ops = &mmc_omap_ops;
mmc->f_min = 400000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_BYTEBLOCK;
+
+ if (minfo->wire4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
/* Use scatterlist DMA to reduce per-transfer costs.
* NOTE max_seg_size assumption that small blocks aren't
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 4e21b3b9d33..fdfc3838dd7 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -1262,7 +1262,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
mmc->ops = &sdhci_ops;
mmc->f_min = host->max_clk / 256;
mmc->f_max = host->max_clk;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
mmc->ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330)
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c
index c351c6d1a18..6435a6822ad 100644
--- a/drivers/mmc/wbsd.c
+++ b/drivers/mmc/wbsd.c
@@ -1323,7 +1323,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
mmc->f_min = 375000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
spin_lock_init(&host->lock);
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index 59c33925be6..b936373ab2a 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -225,6 +225,7 @@ static struct eisa_device_id el3_eisa_ids[] = {
{ "TCM5095" },
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
static int el3_eisa_probe (struct device *device);
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index af301f09d67..df42e28cc80 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -851,6 +851,7 @@ static struct eisa_device_id vortex_eisa_ids[] = {
{ "TCM5970", CH_3C597 },
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, vortex_eisa_ids);
static int vortex_eisa_probe(struct device *device);
static int vortex_eisa_remove(struct device *device);
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index 5b6b05ed8f3..9d34056147a 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -299,7 +299,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
* Slow phase with lock held.
*/
- disable_irq_nosync_lockdep(dev->irq);
+ disable_irq_nosync_lockdep_irqsave(dev->irq, &flags);
spin_lock(&ei_local->page_lock);
@@ -338,7 +338,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
netif_stop_queue(dev);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
spin_unlock(&ei_local->page_lock);
- enable_irq_lockdep(dev->irq);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
ei_local->stat.tx_errors++;
return 1;
}
@@ -379,7 +379,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
spin_unlock(&ei_local->page_lock);
- enable_irq_lockdep(dev->irq);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
dev_kfree_skb (skb);
ei_local->stat.tx_bytes += send_length;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 63154774c25..ff8a8c0a26d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -24,6 +24,9 @@ config NETDEVICES
If unsure, say Y.
+# All the following symbols are dependent on NETDEVICES - do not repeat
+# that for each of the symbols.
+if NETDEVICES
config IFB
tristate "Intermediate Functional Block support"
@@ -2852,6 +2855,8 @@ config NETCONSOLE
If you want to log kernel messages over the network, enable this.
See <file:Documentation/networking/netconsole.txt> for details.
+endif #NETDEVICES
+
config NETPOLL
def_bool NETCONSOLE
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index a075246f6f4..71a4f60f732 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -163,11 +163,7 @@ MODULE_DEVICE_TABLE(pci, acenic_pci_tbl);
#define SET_NETDEV_DEV(net, pdev) do{} while(0)
#endif
-#if LINUX_VERSION_CODE >= 0x2051c
#define ace_sync_irq(irq) synchronize_irq(irq)
-#else
-#define ace_sync_irq(irq) synchronize_irq()
-#endif
#ifndef offset_in_page
#define offset_in_page(ptr) ((unsigned long)(ptr) & ~PAGE_MASK)
diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c
index 7f7dd450226..b98592a8bac 100644
--- a/drivers/net/appletalk/ipddp.c
+++ b/drivers/net/appletalk/ipddp.c
@@ -145,9 +145,7 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
/* Create the Extended DDP header */
ddp = (struct ddpehdr *)skb->data;
- ddp->deh_len = skb->len;
- ddp->deh_hops = 1;
- ddp->deh_pad = 0;
+ ddp->deh_len_hops = htons(skb->len + (1<<10));
ddp->deh_sum = 0;
/*
@@ -170,7 +168,6 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
ddp->deh_sport = 72;
*((__u8 *)(ddp+1)) = 22; /* ddp type = IP */
- *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */
skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */
diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c
index 95b28aa01f4..3ecf2cc53a7 100644
--- a/drivers/net/arm/at91_ether.c
+++ b/drivers/net/arm/at91_ether.c
@@ -947,7 +947,7 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add
return -ENOMEM;
dev->base_addr = AT91_VA_BASE_EMAC;
- dev->irq = AT91_ID_EMAC;
+ dev->irq = AT91RM9200_ID_EMAC;
SET_MODULE_OWNER(dev);
/* Install the interrupt handler */
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 7fcf015021e..6b4edb63c4c 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -56,8 +56,8 @@
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.4.44"
-#define DRV_MODULE_RELDATE "August 10, 2006"
+#define DRV_MODULE_VERSION "1.4.45"
+#define DRV_MODULE_RELDATE "September 29, 2006"
#define RUN_AT(x) (jiffies + (x))
@@ -5805,6 +5805,34 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->cmd_ticks_int = bp->cmd_ticks;
}
+ /* Disable MSI on 5706 if AMD 8132 bridge is found.
+ *
+ * MSI is defined to be 32-bit write. The 5706 does 64-bit MSI writes
+ * with byte enables disabled on the unused 32-bit word. This is legal
+ * but causes problems on the AMD 8132 which will eventually stop
+ * responding after a while.
+ *
+ * AMD believes this incompatibility is unique to the 5706, and
+ * prefers to locally disable MSI rather than globally disabling it
+ * using pci_msi_quirk.
+ */
+ if (CHIP_NUM(bp) == CHIP_NUM_5706 && disable_msi == 0) {
+ struct pci_dev *amd_8132 = NULL;
+
+ while ((amd_8132 = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_8132_BRIDGE,
+ amd_8132))) {
+ u8 rev;
+
+ pci_read_config_byte(amd_8132, PCI_REVISION_ID, &rev);
+ if (rev >= 0x10 && rev <= 0x13) {
+ disable_msi = 1;
+ pci_dev_put(amd_8132);
+ break;
+ }
+ }
+ }
+
bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL;
bp->req_line_speed = 0;
if (bp->phy_flags & PHY_SERDES_FLAG) {
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 6a407070c2e..3fb354d9c51 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -85,6 +85,7 @@
#define AD_LINK_SPEED_BITMASK_10MBPS 0x2
#define AD_LINK_SPEED_BITMASK_100MBPS 0x4
#define AD_LINK_SPEED_BITMASK_1000MBPS 0x8
+#define AD_LINK_SPEED_BITMASK_10000MBPS 0x10
//endalloun
// compare MAC addresses
@@ -99,7 +100,7 @@ static u16 __get_link_speed(struct port *port);
static u8 __get_duplex(struct port *port);
static inline void __initialize_port_locks(struct port *port);
//conversions
-static void __ntohs_lacpdu(struct lacpdu *lacpdu);
+static void __htons_lacpdu(struct lacpdu *lacpdu);
static u16 __ad_timer_to_ticks(u16 timer_type, u16 Par);
@@ -330,7 +331,8 @@ static inline void __release_rx_machine_lock(struct port *port)
* 0,
* %AD_LINK_SPEED_BITMASK_10MBPS,
* %AD_LINK_SPEED_BITMASK_100MBPS,
- * %AD_LINK_SPEED_BITMASK_1000MBPS
+ * %AD_LINK_SPEED_BITMASK_1000MBPS,
+ * %AD_LINK_SPEED_BITMASK_10000MBPS
*/
static u16 __get_link_speed(struct port *port)
{
@@ -357,6 +359,10 @@ static u16 __get_link_speed(struct port *port)
speed = AD_LINK_SPEED_BITMASK_1000MBPS;
break;
+ case SPEED_10000:
+ speed = AD_LINK_SPEED_BITMASK_10000MBPS;
+ break;
+
default:
speed = 0; // unknown speed value from ethtool. shouldn't happen
break;
@@ -414,23 +420,23 @@ static inline void __initialize_port_locks(struct port *port)
//conversions
/**
- * __ntohs_lacpdu - convert the contents of a LACPDU to host byte order
+ * __htons_lacpdu - convert the contents of a LACPDU to network byte order
* @lacpdu: the speicifed lacpdu
*
* For each multi-byte field in the lacpdu, convert its content
*/
-static void __ntohs_lacpdu(struct lacpdu *lacpdu)
+static void __htons_lacpdu(struct lacpdu *lacpdu)
{
if (lacpdu) {
- lacpdu->actor_system_priority = ntohs(lacpdu->actor_system_priority);
- lacpdu->actor_key = ntohs(lacpdu->actor_key);
- lacpdu->actor_port_priority = ntohs(lacpdu->actor_port_priority);
- lacpdu->actor_port = ntohs(lacpdu->actor_port);
- lacpdu->partner_system_priority = ntohs(lacpdu->partner_system_priority);
- lacpdu->partner_key = ntohs(lacpdu->partner_key);
- lacpdu->partner_port_priority = ntohs(lacpdu->partner_port_priority);
- lacpdu->partner_port = ntohs(lacpdu->partner_port);
- lacpdu->collector_max_delay = ntohs(lacpdu->collector_max_delay);
+ lacpdu->actor_system_priority = htons(lacpdu->actor_system_priority);
+ lacpdu->actor_key = htons(lacpdu->actor_key);
+ lacpdu->actor_port_priority = htons(lacpdu->actor_port_priority);
+ lacpdu->actor_port = htons(lacpdu->actor_port);
+ lacpdu->partner_system_priority = htons(lacpdu->partner_system_priority);
+ lacpdu->partner_key = htons(lacpdu->partner_key);
+ lacpdu->partner_port_priority = htons(lacpdu->partner_port_priority);
+ lacpdu->partner_port = htons(lacpdu->partner_port);
+ lacpdu->collector_max_delay = htons(lacpdu->collector_max_delay);
}
}
@@ -490,11 +496,11 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// record the new parameter values for the partner operational
- port->partner_oper_port_number = lacpdu->actor_port;
- port->partner_oper_port_priority = lacpdu->actor_port_priority;
+ port->partner_oper_port_number = ntohs(lacpdu->actor_port);
+ port->partner_oper_port_priority = ntohs(lacpdu->actor_port_priority);
port->partner_oper_system = lacpdu->actor_system;
- port->partner_oper_system_priority = lacpdu->actor_system_priority;
- port->partner_oper_key = lacpdu->actor_key;
+ port->partner_oper_system_priority = ntohs(lacpdu->actor_system_priority);
+ port->partner_oper_key = ntohs(lacpdu->actor_key);
// zero partener's lase states
port->partner_oper_port_state = 0;
port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_LACP_ACTIVITY);
@@ -561,11 +567,11 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// check if any parameter is different
- if ((lacpdu->actor_port != port->partner_oper_port_number) ||
- (lacpdu->actor_port_priority != port->partner_oper_port_priority) ||
+ if ((ntohs(lacpdu->actor_port) != port->partner_oper_port_number) ||
+ (ntohs(lacpdu->actor_port_priority) != port->partner_oper_port_priority) ||
MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->partner_oper_system)) ||
- (lacpdu->actor_system_priority != port->partner_oper_system_priority) ||
- (lacpdu->actor_key != port->partner_oper_key) ||
+ (ntohs(lacpdu->actor_system_priority) != port->partner_oper_system_priority) ||
+ (ntohs(lacpdu->actor_key) != port->partner_oper_key) ||
((lacpdu->actor_state & AD_STATE_AGGREGATION) != (port->partner_oper_port_state & AD_STATE_AGGREGATION))
) {
// update the state machine Selected variable
@@ -628,11 +634,11 @@ static void __choose_matched(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// check if all parameters are alike
- if (((lacpdu->partner_port == port->actor_port_number) &&
- (lacpdu->partner_port_priority == port->actor_port_priority) &&
+ if (((ntohs(lacpdu->partner_port) == port->actor_port_number) &&
+ (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) &&
!MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) &&
- (lacpdu->partner_system_priority == port->actor_system_priority) &&
- (lacpdu->partner_key == port->actor_oper_port_key) &&
+ (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) &&
+ (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) &&
((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) ||
// or this is individual link(aggregation == FALSE)
((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0)
@@ -662,11 +668,11 @@ static void __update_ntt(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// check if any parameter is different
- if ((lacpdu->partner_port != port->actor_port_number) ||
- (lacpdu->partner_port_priority != port->actor_port_priority) ||
+ if ((ntohs(lacpdu->partner_port) != port->actor_port_number) ||
+ (ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) ||
MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) ||
- (lacpdu->partner_system_priority != port->actor_system_priority) ||
- (lacpdu->partner_key != port->actor_oper_port_key) ||
+ (ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) ||
+ (ntohs(lacpdu->partner_key) != port->actor_oper_port_key) ||
((lacpdu->partner_state & AD_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY)) ||
((lacpdu->partner_state & AD_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)) ||
((lacpdu->partner_state & AD_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) ||
@@ -775,6 +781,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator)
case AD_LINK_SPEED_BITMASK_1000MBPS:
bandwidth = aggregator->num_of_ports * 1000;
break;
+ case AD_LINK_SPEED_BITMASK_10000MBPS:
+ bandwidth = aggregator->num_of_ports * 10000;
+ break;
default:
bandwidth=0; // to silent the compilor ....
}
@@ -847,7 +856,7 @@ static inline void __update_lacpdu_from_port(struct port *port)
*/
/* Convert all non u8 parameters to Big Endian for transmit */
- __ntohs_lacpdu(lacpdu);
+ __htons_lacpdu(lacpdu);
}
//////////////////////////////////////////////////////////////////////////////////////
@@ -2171,7 +2180,6 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u
switch (lacpdu->subtype) {
case AD_TYPE_LACPDU:
- __ntohs_lacpdu(lacpdu);
dprintk("Received LACPDU on port %d\n", port->actor_port_number);
ad_rx_machine(lacpdu, port);
break;
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 850aae21a2f..c0bbddae4ec 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -96,6 +96,7 @@ static char *lacp_rate = NULL;
static char *xmit_hash_policy = NULL;
static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[BOND_MAX_ARP_TARGETS] = { NULL, };
+static char *arp_validate = NULL;
struct bond_params bonding_defaults;
module_param(max_bonds, int, 0);
@@ -127,6 +128,8 @@ module_param(arp_interval, int, 0);
MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
module_param_array(arp_ip_target, charp, NULL, 0);
MODULE_PARM_DESC(arp_ip_target, "arp targets in n.n.n.n form");
+module_param(arp_validate, charp, 0);
+MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes: none (default), active, backup or all");
/*----------------------------- Global variables ----------------------------*/
@@ -170,6 +173,14 @@ struct bond_parm_tbl xmit_hashtype_tbl[] = {
{ NULL, -1},
};
+struct bond_parm_tbl arp_validate_tbl[] = {
+{ "none", BOND_ARP_VALIDATE_NONE},
+{ "active", BOND_ARP_VALIDATE_ACTIVE},
+{ "backup", BOND_ARP_VALIDATE_BACKUP},
+{ "all", BOND_ARP_VALIDATE_ALL},
+{ NULL, -1},
+};
+
/*-------------------------- Forward declarations ---------------------------*/
static void bond_send_gratuitous_arp(struct bonding *bond);
@@ -638,6 +649,7 @@ verify:
case SPEED_10:
case SPEED_100:
case SPEED_1000:
+ case SPEED_10000:
break;
default:
return -1;
@@ -1210,10 +1222,14 @@ static int bond_compute_features(struct bonding *bond)
unsigned long features = BOND_INTERSECT_FEATURES;
struct slave *slave;
struct net_device *bond_dev = bond->dev;
+ unsigned short max_hard_header_len = ETH_HLEN;
int i;
- bond_for_each_slave(bond, slave, i)
+ bond_for_each_slave(bond, slave, i) {
features &= (slave->dev->features & BOND_INTERSECT_FEATURES);
+ if (slave->dev->hard_header_len > max_hard_header_len)
+ max_hard_header_len = slave->dev->hard_header_len;
+ }
if ((features & NETIF_F_SG) &&
!(features & NETIF_F_ALL_CSUM))
@@ -1231,6 +1247,7 @@ static int bond_compute_features(struct bonding *bond)
features |= (bond_dev->features & ~BOND_INTERSECT_FEATURES);
bond_dev->features = features;
+ bond_dev->hard_header_len = max_hard_header_len;
return 0;
}
@@ -1365,6 +1382,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
}
new_slave->dev = slave_dev;
+ slave_dev->priv_flags |= IFF_BONDING;
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
@@ -1417,6 +1435,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_compute_features(bond);
+ new_slave->last_arp_rx = jiffies;
+
if (bond->params.miimon && !bond->params.use_carrier) {
link_reporting = bond_check_dev_link(bond, slave_dev, 1);
@@ -1493,29 +1513,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
switch (bond->params.mode) {
case BOND_MODE_ACTIVEBACKUP:
- /* if we're in active-backup mode, we need one and
- * only one active interface. The backup interfaces
- * will have their SLAVE_INACTIVE flag set because we
- * need them to be drop all packets. Thus, since we
- * guarantee that curr_active_slave always point to
- * the last usable interface, we just have to verify
- * this interface's flag.
- */
- if (((!bond->curr_active_slave) ||
- (bond->curr_active_slave->dev->priv_flags & IFF_SLAVE_INACTIVE)) &&
- (new_slave->link != BOND_LINK_DOWN)) {
- /* first slave or no active slave yet, and this link
- is OK, so make this interface the active one */
- bond_change_active_slave(bond, new_slave);
- printk(KERN_INFO DRV_NAME
- ": %s: first active interface up!\n",
- bond->dev->name);
- netif_carrier_on(bond->dev);
-
- } else {
- dprintk("This is just a backup slave\n");
- bond_set_slave_inactive_flags(new_slave);
- }
+ bond_set_slave_inactive_flags(new_slave);
+ bond_select_active_slave(bond);
break;
case BOND_MODE_8023AD:
/* in 802.3ad mode, the internal mechanism
@@ -1778,7 +1777,8 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
dev_set_mac_address(slave_dev, &addr);
slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
- IFF_SLAVE_INACTIVE);
+ IFF_SLAVE_INACTIVE | IFF_BONDING |
+ IFF_SLAVE_NEEDARP);
kfree(slave);
@@ -2252,7 +2252,7 @@ static u32 bond_glean_dev_ip(struct net_device *dev)
{
struct in_device *idev;
struct in_ifaddr *ifa;
- u32 addr = 0;
+ __be32 addr = 0;
if (!dev)
return 0;
@@ -2291,6 +2291,25 @@ static int bond_has_ip(struct bonding *bond)
return 0;
}
+static int bond_has_this_ip(struct bonding *bond, u32 ip)
+{
+ struct vlan_entry *vlan, *vlan_next;
+
+ if (ip == bond->master_ip)
+ return 1;
+
+ if (list_empty(&bond->vlan_list))
+ return 0;
+
+ list_for_each_entry_safe(vlan, vlan_next, &bond->vlan_list,
+ vlan_list) {
+ if (ip == vlan->vlan_ip)
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* We go to the (large) trouble of VLAN tagging ARP frames because
* switches in VLAN mode (especially if ports are configured as
@@ -2429,6 +2448,93 @@ static void bond_send_gratuitous_arp(struct bonding *bond)
}
}
+static void bond_validate_arp(struct bonding *bond, struct slave *slave, u32 sip, u32 tip)
+{
+ int i;
+ u32 *targets = bond->params.arp_targets;
+
+ targets = bond->params.arp_targets;
+ for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
+ dprintk("bva: sip %u.%u.%u.%u tip %u.%u.%u.%u t[%d] "
+ "%u.%u.%u.%u bhti(tip) %d\n",
+ NIPQUAD(sip), NIPQUAD(tip), i, NIPQUAD(targets[i]),
+ bond_has_this_ip(bond, tip));
+ if (sip == targets[i]) {
+ if (bond_has_this_ip(bond, tip))
+ slave->last_arp_rx = jiffies;
+ return;
+ }
+ }
+}
+
+static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct arphdr *arp;
+ struct slave *slave;
+ struct bonding *bond;
+ unsigned char *arp_ptr;
+ u32 sip, tip;
+
+ if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER))
+ goto out;
+
+ bond = dev->priv;
+ read_lock(&bond->lock);
+
+ dprintk("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
+ bond->dev->name, skb->dev ? skb->dev->name : "NULL",
+ orig_dev ? orig_dev->name : "NULL");
+
+ slave = bond_get_slave_by_dev(bond, orig_dev);
+ if (!slave || !slave_do_arp_validate(bond, slave))
+ goto out_unlock;
+
+ /* ARP header, plus 2 device addresses, plus 2 IP addresses. */
+ if (!pskb_may_pull(skb, (sizeof(struct arphdr) +
+ (2 * dev->addr_len) +
+ (2 * sizeof(u32)))))
+ goto out_unlock;
+
+ arp = skb->nh.arph;
+ if (arp->ar_hln != dev->addr_len ||
+ skb->pkt_type == PACKET_OTHERHOST ||
+ skb->pkt_type == PACKET_LOOPBACK ||
+ arp->ar_hrd != htons(ARPHRD_ETHER) ||
+ arp->ar_pro != htons(ETH_P_IP) ||
+ arp->ar_pln != 4)
+ goto out_unlock;
+
+ arp_ptr = (unsigned char *)(arp + 1);
+ arp_ptr += dev->addr_len;
+ memcpy(&sip, arp_ptr, 4);
+ arp_ptr += 4 + dev->addr_len;
+ memcpy(&tip, arp_ptr, 4);
+
+ dprintk("bond_arp_rcv: %s %s/%d av %d sv %d sip %u.%u.%u.%u"
+ " tip %u.%u.%u.%u\n", bond->dev->name, slave->dev->name,
+ slave->state, bond->params.arp_validate,
+ slave_do_arp_validate(bond, slave), NIPQUAD(sip), NIPQUAD(tip));
+
+ /*
+ * Backup slaves won't see the ARP reply, but do come through
+ * here for each ARP probe (so we swap the sip/tip to validate
+ * the probe). In a "redundant switch, common router" type of
+ * configuration, the ARP probe will (hopefully) travel from
+ * the active, through one switch, the router, then the other
+ * switch before reaching the backup.
+ */
+ if (slave->state == BOND_STATE_ACTIVE)
+ bond_validate_arp(bond, slave, sip, tip);
+ else
+ bond_validate_arp(bond, slave, tip, sip);
+
+out_unlock:
+ read_unlock(&bond->lock);
+out:
+ dev_kfree_skb(skb);
+ return NET_RX_SUCCESS;
+}
+
/*
* this function is called regularly to monitor each slave's link
* ensuring that traffic is being sent and received when arp monitoring
@@ -2593,7 +2699,8 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev)
*/
bond_for_each_slave(bond, slave, i) {
if (slave->link != BOND_LINK_UP) {
- if ((jiffies - slave->dev->last_rx) <= delta_in_ticks) {
+ if ((jiffies - slave_last_rx(bond, slave)) <=
+ delta_in_ticks) {
slave->link = BOND_LINK_UP;
@@ -2638,7 +2745,7 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev)
if ((slave != bond->curr_active_slave) &&
(!bond->current_arp_slave) &&
- (((jiffies - slave->dev->last_rx) >= 3*delta_in_ticks) &&
+ (((jiffies - slave_last_rx(bond, slave)) >= 3*delta_in_ticks) &&
bond_has_ip(bond))) {
/* a backup slave has gone down; three times
* the delta allows the current slave to be
@@ -2685,7 +2792,7 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev)
* if it is up and needs to take over as the curr_active_slave
*/
if ((((jiffies - slave->dev->trans_start) >= (2*delta_in_ticks)) ||
- (((jiffies - slave->dev->last_rx) >= (2*delta_in_ticks)) &&
+ (((jiffies - slave_last_rx(bond, slave)) >= (2*delta_in_ticks)) &&
bond_has_ip(bond))) &&
((jiffies - slave->jiffies) >= 2*delta_in_ticks)) {
@@ -2950,7 +3057,7 @@ static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
seq_printf(seq, "MII Status: %s\n",
(slave->link == BOND_LINK_UP) ? "up" : "down");
- seq_printf(seq, "Link Failure Count: %d\n",
+ seq_printf(seq, "Link Failure Count: %u\n",
slave->link_failure_count);
seq_printf(seq,
@@ -3210,6 +3317,9 @@ static int bond_netdev_event(struct notifier_block *this, unsigned long event, v
(event_dev ? event_dev->name : "None"),
event);
+ if (!(event_dev->priv_flags & IFF_BONDING))
+ return NOTIFY_DONE;
+
if (event_dev->flags & IFF_MASTER) {
dprintk("IFF_MASTER\n");
return bond_master_netdev_event(event, event_dev);
@@ -3305,6 +3415,21 @@ static void bond_unregister_lacpdu(struct bonding *bond)
dev_remove_pack(&(BOND_AD_INFO(bond).ad_pkt_type));
}
+void bond_register_arp(struct bonding *bond)
+{
+ struct packet_type *pt = &bond->arp_mon_pt;
+
+ pt->type = htons(ETH_P_ARP);
+ pt->dev = NULL; /*bond->dev;XXX*/
+ pt->func = bond_arp_rcv;
+ dev_add_pack(pt);
+}
+
+void bond_unregister_arp(struct bonding *bond)
+{
+ dev_remove_pack(&bond->arp_mon_pt);
+}
+
/*---------------------------- Hashing Policies -----------------------------*/
/*
@@ -3391,6 +3516,9 @@ static int bond_open(struct net_device *bond_dev)
} else {
arp_timer->function = (void *)&bond_loadbalance_arp_mon;
}
+ if (bond->params.arp_validate)
+ bond_register_arp(bond);
+
add_timer(arp_timer);
}
@@ -3418,9 +3546,11 @@ static int bond_close(struct net_device *bond_dev)
bond_unregister_lacpdu(bond);
}
+ if (bond->params.arp_validate)
+ bond_unregister_arp(bond);
+
write_lock_bh(&bond->lock);
- bond_mc_list_destroy(bond);
/* signal timers not to re-arm */
bond->kill_timers = 1;
@@ -3451,8 +3581,6 @@ static int bond_close(struct net_device *bond_dev)
break;
}
- /* Release the bonded slaves */
- bond_release_all(bond_dev);
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
@@ -4179,6 +4307,7 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
/* Initialize the device options */
bond_dev->tx_queue_len = 0;
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
+ bond_dev->priv_flags |= IFF_BONDING;
/* At first, we block adding VLANs. That's the only way to
* prevent problems that occur when adding VLANs over an
@@ -4237,6 +4366,9 @@ static void bond_free_all(void)
list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
struct net_device *bond_dev = bond->dev;
+ bond_mc_list_destroy(bond);
+ /* Release the bonded slaves */
+ bond_release_all(bond_dev);
unregister_netdevice(bond_dev);
bond_deinit(bond_dev);
}
@@ -4270,6 +4402,8 @@ int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl)
static int bond_check_params(struct bond_params *params)
{
+ int arp_validate_value;
+
/*
* Convert string parameters.
*/
@@ -4473,6 +4607,29 @@ static int bond_check_params(struct bond_params *params)
arp_interval = 0;
}
+ if (arp_validate) {
+ if (bond_mode != BOND_MODE_ACTIVEBACKUP) {
+ printk(KERN_ERR DRV_NAME
+ ": arp_validate only supported in active-backup mode\n");
+ return -EINVAL;
+ }
+ if (!arp_interval) {
+ printk(KERN_ERR DRV_NAME
+ ": arp_validate requires arp_interval\n");
+ return -EINVAL;
+ }
+
+ arp_validate_value = bond_parse_parm(arp_validate,
+ arp_validate_tbl);
+ if (arp_validate_value == -1) {
+ printk(KERN_ERR DRV_NAME
+ ": Error: invalid arp_validate \"%s\"\n",
+ arp_validate == NULL ? "NULL" : arp_validate);
+ return -EINVAL;
+ }
+ } else
+ arp_validate_value = 0;
+
if (miimon) {
printk(KERN_INFO DRV_NAME
": MII link monitoring set to %d ms\n",
@@ -4481,8 +4638,10 @@ static int bond_check_params(struct bond_params *params)
int i;
printk(KERN_INFO DRV_NAME
- ": ARP monitoring set to %d ms with %d target(s):",
- arp_interval, arp_ip_count);
+ ": ARP monitoring set to %d ms, validate %s, with %d target(s):",
+ arp_interval,
+ arp_validate_tbl[arp_validate_value].modename,
+ arp_ip_count);
for (i = 0; i < arp_ip_count; i++)
printk (" %s", arp_ip_target[i]);
@@ -4516,6 +4675,7 @@ static int bond_check_params(struct bond_params *params)
params->xmit_policy = xmit_hashtype;
params->miimon = miimon;
params->arp_interval = arp_interval;
+ params->arp_validate = arp_validate_value;
params->updelay = updelay;
params->downdelay = downdelay;
params->use_carrier = use_carrier;
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index cfe4dc3a93a..ced9ed8f995 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -51,6 +51,7 @@ extern struct bond_params bonding_defaults;
extern struct bond_parm_tbl bond_mode_tbl[];
extern struct bond_parm_tbl bond_lacp_tbl[];
extern struct bond_parm_tbl xmit_hashtype_tbl[];
+extern struct bond_parm_tbl arp_validate_tbl[];
static int expected_refcount = -1;
static struct class *netdev_class;
@@ -503,6 +504,53 @@ out:
static CLASS_DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR, bonding_show_xmit_hash, bonding_store_xmit_hash);
/*
+ * Show and set arp_validate.
+ */
+static ssize_t bonding_show_arp_validate(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%s %d\n",
+ arp_validate_tbl[bond->params.arp_validate].modename,
+ bond->params.arp_validate) + 1;
+}
+
+static ssize_t bonding_store_arp_validate(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value;
+ struct bonding *bond = to_bond(cd);
+
+ new_value = bond_parse_parm((char *)buf, arp_validate_tbl);
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Ignoring invalid arp_validate value %s\n",
+ bond->dev->name, buf);
+ return -EINVAL;
+ }
+ if (new_value && (bond->params.mode != BOND_MODE_ACTIVEBACKUP)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: arp_validate only supported in active-backup mode.\n",
+ bond->dev->name);
+ return -EINVAL;
+ }
+ printk(KERN_INFO DRV_NAME ": %s: setting arp_validate to %s (%d).\n",
+ bond->dev->name, arp_validate_tbl[new_value].modename,
+ new_value);
+
+ if (!bond->params.arp_validate && new_value) {
+ bond_register_arp(bond);
+ } else if (bond->params.arp_validate && !new_value) {
+ bond_unregister_arp(bond);
+ }
+
+ bond->params.arp_validate = new_value;
+
+ return count;
+}
+
+static CLASS_DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate, bonding_store_arp_validate);
+
+/*
* Show and set the arp timer interval. There are two tricky bits
* here. First, if ARP monitoring is activated, then we must disable
* MII monitoring. Second, if the ARP timer isn't running, we must
@@ -914,6 +962,11 @@ static ssize_t bonding_store_miimon(struct class_device *cd, const char *buf, si
"ARP monitoring. Disabling ARP monitoring...\n",
bond->dev->name);
bond->params.arp_interval = 0;
+ if (bond->params.arp_validate) {
+ bond_unregister_arp(bond);
+ bond->params.arp_validate =
+ BOND_ARP_VALIDATE_NONE;
+ }
/* Kill ARP timer, else it brings bond's link down */
if (bond->mii_timer.function) {
printk(KERN_INFO DRV_NAME
@@ -1093,7 +1146,7 @@ static ssize_t bonding_store_active_slave(struct class_device *cd, const char *b
strlen(slave->dev->name)) == 0) {
old_active = bond->curr_active_slave;
new_active = slave;
- if (new_active && (new_active == old_active)) {
+ if (new_active == old_active) {
/* do nothing */
printk(KERN_INFO DRV_NAME
": %s: %s is already the current active slave.\n",
@@ -1273,6 +1326,7 @@ static CLASS_DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, N
static struct attribute *per_bond_attrs[] = {
&class_device_attr_slaves.attr,
&class_device_attr_mode.attr,
+ &class_device_attr_arp_validate.attr,
&class_device_attr_arp_interval.attr,
&class_device_attr_arp_ip_target.attr,
&class_device_attr_downdelay.attr,
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 0bdfe2c7145..dc434fb6da8 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -22,8 +22,8 @@
#include "bond_3ad.h"
#include "bond_alb.h"
-#define DRV_VERSION "3.0.3"
-#define DRV_RELDATE "March 23, 2006"
+#define DRV_VERSION "3.1.1"
+#define DRV_RELDATE "September 26, 2006"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
@@ -126,6 +126,7 @@ struct bond_params {
int xmit_policy;
int miimon;
int arp_interval;
+ int arp_validate;
int use_carrier;
int updelay;
int downdelay;
@@ -149,8 +150,9 @@ struct slave {
struct net_device *dev; /* first - useful for panic debug */
struct slave *next;
struct slave *prev;
- s16 delay;
+ int delay;
u32 jiffies;
+ u32 last_arp_rx;
s8 link; /* one of BOND_LINK_XXXX */
s8 state; /* one of BOND_STATE_XXXX */
u32 original_flags;
@@ -198,6 +200,7 @@ struct bonding {
struct bond_params params;
struct list_head vlan_list;
struct vlan_group *vlgrp;
+ struct packet_type arp_mon_pt;
};
/**
@@ -228,6 +231,25 @@ static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
return (struct bonding *)slave->dev->master->priv;
}
+#define BOND_ARP_VALIDATE_NONE 0
+#define BOND_ARP_VALIDATE_ACTIVE (1 << BOND_STATE_ACTIVE)
+#define BOND_ARP_VALIDATE_BACKUP (1 << BOND_STATE_BACKUP)
+#define BOND_ARP_VALIDATE_ALL (BOND_ARP_VALIDATE_ACTIVE | \
+ BOND_ARP_VALIDATE_BACKUP)
+
+extern inline int slave_do_arp_validate(struct bonding *bond, struct slave *slave)
+{
+ return bond->params.arp_validate & (1 << slave->state);
+}
+
+extern inline u32 slave_last_rx(struct bonding *bond, struct slave *slave)
+{
+ if (slave_do_arp_validate(bond, slave))
+ return slave->last_arp_rx;
+
+ return slave->dev->last_rx;
+}
+
static inline void bond_set_slave_inactive_flags(struct slave *slave)
{
struct bonding *bond = slave->dev->master->priv;
@@ -235,12 +257,14 @@ static inline void bond_set_slave_inactive_flags(struct slave *slave)
bond->params.mode != BOND_MODE_ALB)
slave->state = BOND_STATE_BACKUP;
slave->dev->priv_flags |= IFF_SLAVE_INACTIVE;
+ if (slave_do_arp_validate(bond, slave))
+ slave->dev->priv_flags |= IFF_SLAVE_NEEDARP;
}
static inline void bond_set_slave_active_flags(struct slave *slave)
{
slave->state = BOND_STATE_ACTIVE;
- slave->dev->priv_flags &= ~IFF_SLAVE_INACTIVE;
+ slave->dev->priv_flags &= ~(IFF_SLAVE_INACTIVE | IFF_SLAVE_NEEDARP);
}
static inline void bond_set_master_3ad_flags(struct bonding *bond)
@@ -284,6 +308,8 @@ int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl);
const char *bond_mode_name(int mode);
void bond_select_active_slave(struct bonding *bond);
void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
+void bond_register_arp(struct bonding *);
+void bond_unregister_arp(struct bonding *);
#endif /* _LINUX_BONDING_H */
diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h
index a170e96251f..4020acb5500 100644
--- a/drivers/net/e1000/e1000_hw.h
+++ b/drivers/net/e1000/e1000_hw.h
@@ -1367,8 +1367,8 @@ struct e1000_hw_stats {
/* Structure containing variables used by the shared code (e1000_hw.c) */
struct e1000_hw {
- uint8_t *hw_addr;
- uint8_t *flash_address;
+ uint8_t __iomem *hw_addr;
+ uint8_t __iomem *flash_address;
e1000_mac_type mac_type;
e1000_phy_type phy_type;
uint32_t phy_init_script;
diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c
index 22ac2df1aeb..e17a1449ee1 100644
--- a/drivers/net/fec_8xx/fec_main.c
+++ b/drivers/net/fec_8xx/fec_main.c
@@ -30,6 +30,7 @@
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
#include <asm/8xx_immap.h>
#include <asm/pgtable.h>
@@ -37,7 +38,6 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/commproc.h>
-#include <asm/dma-mapping.h>
#include "fec_8xx.h"
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 97db910fbc8..eea1d66c530 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -3789,6 +3789,12 @@ static int nv_loopback_test(struct net_device *dev)
/* setup packet for tx */
pkt_len = ETH_DATA_LEN;
tx_skb = dev_alloc_skb(pkt_len);
+ if (!tx_skb) {
+ printk(KERN_ERR "dev_alloc_skb() failed during loopback test"
+ " of %s\n", dev->name);
+ ret = 0;
+ goto out;
+ }
pkt_data = skb_put(tx_skb, pkt_len);
for (i = 0; i < pkt_len; i++)
pkt_data[i] = (u8)(i & 0xff);
@@ -3853,7 +3859,7 @@ static int nv_loopback_test(struct net_device *dev)
tx_skb->end-tx_skb->data,
PCI_DMA_TODEVICE);
dev_kfree_skb_any(tx_skb);
-
+ out:
/* stop engines */
nv_stop_rx(dev);
nv_stop_tx(dev);
diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/fs_enet/fs_enet.h
index 95022c005f7..92590d8fc24 100644
--- a/drivers/net/fs_enet/fs_enet.h
+++ b/drivers/net/fs_enet/fs_enet.h
@@ -6,11 +6,10 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/phy.h>
+#include <linux/dma-mapping.h>
#include <linux/fs_enet_pd.h>
-#include <asm/dma-mapping.h>
-
#ifdef CONFIG_CPM1
#include <asm/commproc.h>
diff --git a/drivers/net/gt64240eth.h b/drivers/net/gt64240eth.h
deleted file mode 100644
index 0d6f486e457..00000000000
--- a/drivers/net/gt64240eth.h
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * 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.
- *
- * Copyright (C) 2001 Patton Electronics Company
- * Copyright (C) 2002 Momentum Computer
- *
- * Copyright 2000 MontaVista Software Inc.
- * Author: MontaVista Software, Inc.
- * stevel@mvista.com or support@mvista.com
- *
- * This program is free software; you can distribute it and/or modify it
- * under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
- *
- * Ethernet driver definitions for the MIPS GT96100 Advanced
- * Communication Controller.
- *
- * Modified for the Marvellous GT64240 Retarded Communication Controller.
- */
-#ifndef _GT64240ETH_H
-#define _GT64240ETH_H
-
-#include <asm/gt64240.h>
-
-#define ETHERNET_PORTS_DIFFERENCE_OFFSETS 0x400
-
-/* Translate those weanie names from Galileo/VxWorks header files: */
-
-#define GT64240_MRR MAIN_ROUTING_REGISTER
-#define GT64240_CIU_ARBITER_CONFIG COMM_UNIT_ARBITER_CONFIGURATION_REGISTER
-#define GT64240_CIU_ARBITER_CONTROL COMM_UNIT_ARBITER_CONTROL
-#define GT64240_MAIN_LOW_CAUSE LOW_INTERRUPT_CAUSE_REGISTER
-#define GT64240_MAIN_HIGH_CAUSE HIGH_INTERRUPT_CAUSE_REGISTER
-#define GT64240_CPU_LOW_MASK CPU_INTERRUPT_MASK_REGISTER_LOW
-#define GT64240_CPU_HIGH_MASK CPU_INTERRUPT_MASK_REGISTER_HIGH
-#define GT64240_CPU_SELECT_CAUSE CPU_SELECT_CAUSE_REGISTER
-
-#define GT64240_ETH_PHY_ADDR_REG ETHERNET_PHY_ADDRESS_REGISTER
-#define GT64240_ETH_PORT_CONFIG ETHERNET0_PORT_CONFIGURATION_REGISTER
-#define GT64240_ETH_PORT_CONFIG_EXT ETHERNET0_PORT_CONFIGURATION_EXTEND_REGISTER
-#define GT64240_ETH_PORT_COMMAND ETHERNET0_PORT_COMMAND_REGISTER
-#define GT64240_ETH_PORT_STATUS ETHERNET0_PORT_STATUS_REGISTER
-#define GT64240_ETH_IO_SIZE ETHERNET_PORTS_DIFFERENCE_OFFSETS
-#define GT64240_ETH_SMI_REG ETHERNET_SMI_REGISTER
-#define GT64240_ETH_MIB_COUNT_BASE ETHERNET0_MIB_COUNTER_BASE
-#define GT64240_ETH_SDMA_CONFIG ETHERNET0_SDMA_CONFIGURATION_REGISTER
-#define GT64240_ETH_SDMA_COMM ETHERNET0_SDMA_COMMAND_REGISTER
-#define GT64240_ETH_INT_MASK ETHERNET0_INTERRUPT_MASK_REGISTER
-#define GT64240_ETH_INT_CAUSE ETHERNET0_INTERRUPT_CAUSE_REGISTER
-#define GT64240_ETH_CURR_TX_DESC_PTR0 ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER0
-#define GT64240_ETH_CURR_TX_DESC_PTR1 ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER1
-#define GT64240_ETH_1ST_RX_DESC_PTR0 ETHERNET0_FIRST_RX_DESCRIPTOR_POINTER0
-#define GT64240_ETH_CURR_RX_DESC_PTR0 ETHERNET0_CURRENT_RX_DESCRIPTOR_POINTER0
-#define GT64240_ETH_HASH_TBL_PTR ETHERNET0_HASH_TABLE_POINTER_REGISTER
-
-/* Turn on NAPI by default */
-
-#define GT64240_NAPI 1
-
-/* Some 64240 settings that SHOULD eventually be setup in PROM monitor: */
-/* (Board-specific to the DSL3224 Rev A board ONLY!) */
-#define D3224_MPP_CTRL0_SETTING 0x66669900
-#define D3224_MPP_CTRL1_SETTING 0x00000000
-#define D3224_MPP_CTRL2_SETTING 0x00887700
-#define D3224_MPP_CTRL3_SETTING 0x00000044
-#define D3224_GPP_IO_CTRL_SETTING 0x0000e800
-#define D3224_GPP_LEVEL_CTRL_SETTING 0xf001f703
-#define D3224_GPP_VALUE_SETTING 0x00000000
-
-/* Keep the ring sizes a power of two for efficiency. */
-//-#define TX_RING_SIZE 16
-#define TX_RING_SIZE 64 /* TESTING !!! */
-#define RX_RING_SIZE 32
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
-
-#define RX_HASH_TABLE_SIZE 16384
-#define HASH_HOP_NUMBER 12
-
-#define NUM_INTERFACES 3
-
-#define GT64240ETH_TX_TIMEOUT HZ/4
-
-#define MIPS_GT64240_BASE 0xf4000000
-#define GT64240_ETH0_BASE (MIPS_GT64240_BASE + GT64240_ETH_PORT_CONFIG)
-#define GT64240_ETH1_BASE (GT64240_ETH0_BASE + GT64240_ETH_IO_SIZE)
-#define GT64240_ETH2_BASE (GT64240_ETH1_BASE + GT64240_ETH_IO_SIZE)
-
-#if defined(CONFIG_MIPS_DSL3224)
-#define GT64240_ETHER0_IRQ 4
-#define GT64240_ETHER1_IRQ 4
-#else
-#define GT64240_ETHER0_IRQ -1
-#define GT64240_ETHER1_IRQ -1
-#endif
-
-#define REV_GT64240 0x1
-#define REV_GT64240A 0x10
-
-#define GT64240ETH_READ(gp, offset) \
- GT_READ((gp)->port_offset + (offset))
-
-#define GT64240ETH_WRITE(gp, offset, data) \
- GT_WRITE((gp)->port_offset + (offset), (data))
-
-#define GT64240ETH_SETBIT(gp, offset, bits) \
- GT64240ETH_WRITE((gp), (offset), \
- GT64240ETH_READ((gp), (offset)) | (bits))
-
-#define GT64240ETH_CLRBIT(gp, offset, bits) \
- GT64240ETH_WRITE((gp), (offset), \
- GT64240ETH_READ((gp), (offset)) & ~(bits))
-
-#define GT64240_READ(ofs) GT_READ(ofs)
-#define GT64240_WRITE(ofs, data) GT_WRITE((ofs), (data))
-
-/* Bit definitions of the SMI Reg */
-enum {
- smirDataMask = 0xffff,
- smirPhyAdMask = 0x1f << 16,
- smirPhyAdBit = 16,
- smirRegAdMask = 0x1f << 21,
- smirRegAdBit = 21,
- smirOpCode = 1 << 26,
- smirReadValid = 1 << 27,
- smirBusy = 1 << 28
-};
-
-/* Bit definitions of the Port Config Reg */
-enum pcr_bits {
- pcrPM = 1 << 0,
- pcrRBM = 1 << 1,
- pcrPBF = 1 << 2,
- pcrEN = 1 << 7,
- pcrLPBKMask = 0x3 << 8,
- pcrLPBKBit = 1 << 8,
- pcrFC = 1 << 10,
- pcrHS = 1 << 12,
- pcrHM = 1 << 13,
- pcrHDM = 1 << 14,
- pcrHD = 1 << 15,
- pcrISLMask = 0x7 << 28,
- pcrISLBit = 28,
- pcrACCS = 1 << 31
-};
-
-/* Bit definitions of the Port Config Extend Reg */
-enum pcxr_bits {
- pcxrIGMP = 1,
- pcxrSPAN = 2,
- pcxrPAR = 4,
- pcxrPRIOtxMask = 0x7 << 3,
- pcxrPRIOtxBit = 3,
- pcxrPRIOrxMask = 0x3 << 6,
- pcxrPRIOrxBit = 6,
- pcxrPRIOrxOverride = 1 << 8,
- pcxrDPLXen = 1 << 9,
- pcxrFCTLen = 1 << 10,
- pcxrFLP = 1 << 11,
- pcxrFCTL = 1 << 12,
- pcxrMFLMask = 0x3 << 14,
- pcxrMFLBit = 14,
- pcxrMIBclrMode = 1 << 16,
- pcxrSpeed = 1 << 18,
- pcxrSpeeden = 1 << 19,
- pcxrRMIIen = 1 << 20,
- pcxrDSCPen = 1 << 21
-};
-
-/* Bit definitions of the Port Command Reg */
-enum pcmr_bits {
- pcmrFJ = 1 << 15
-};
-
-
-/* Bit definitions of the Port Status Reg */
-enum psr_bits {
- psrSpeed = 1,
- psrDuplex = 2,
- psrFctl = 4,
- psrLink = 8,
- psrPause = 1 << 4,
- psrTxLow = 1 << 5,
- psrTxHigh = 1 << 6,
- psrTxInProg = 1 << 7
-};
-
-/* Bit definitions of the SDMA Config Reg */
-enum sdcr_bits {
- sdcrRCMask = 0xf << 2,
- sdcrRCBit = 2,
- sdcrBLMR = 1 << 6,
- sdcrBLMT = 1 << 7,
- sdcrPOVR = 1 << 8,
- sdcrRIFB = 1 << 9,
- sdcrBSZMask = 0x3 << 12,
- sdcrBSZBit = 12
-};
-
-/* Bit definitions of the SDMA Command Reg */
-enum sdcmr_bits {
- sdcmrERD = 1 << 7,
- sdcmrAR = 1 << 15,
- sdcmrSTDH = 1 << 16,
- sdcmrSTDL = 1 << 17,
- sdcmrTXDH = 1 << 23,
- sdcmrTXDL = 1 << 24,
- sdcmrAT = 1 << 31
-};
-
-/* Bit definitions of the Interrupt Cause Reg */
-enum icr_bits {
- icrRxBuffer = 1,
- icrTxBufferHigh = 1 << 2,
- icrTxBufferLow = 1 << 3,
- icrTxEndHigh = 1 << 6,
- icrTxEndLow = 1 << 7,
- icrRxError = 1 << 8,
- icrTxErrorHigh = 1 << 10,
- icrTxErrorLow = 1 << 11,
- icrRxOVR = 1 << 12,
- icrTxUdr = 1 << 13,
- icrRxBufferQ0 = 1 << 16,
- icrRxBufferQ1 = 1 << 17,
- icrRxBufferQ2 = 1 << 18,
- icrRxBufferQ3 = 1 << 19,
- icrRxErrorQ0 = 1 << 20,
- icrRxErrorQ1 = 1 << 21,
- icrRxErrorQ2 = 1 << 22,
- icrRxErrorQ3 = 1 << 23,
- icrMIIPhySTC = 1 << 28,
- icrSMIdone = 1 << 29,
- icrEtherIntSum = 1 << 31
-};
-
-
-/* The Rx and Tx descriptor lists. */
-#ifdef __LITTLE_ENDIAN
-typedef struct {
- u32 cmdstat;
- u16 reserved; //-prk21aug01 u32 reserved:16;
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u32 buff_ptr;
- u32 next;
-} gt64240_td_t;
-
-typedef struct {
- u32 cmdstat;
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u16 buff_sz; //-prk21aug01 u32 buff_sz:16;
- u32 buff_ptr;
- u32 next;
-} gt64240_rd_t;
-#elif defined(__BIG_ENDIAN)
-typedef struct {
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u16 reserved; //-prk21aug01 u32 reserved:16;
- u32 cmdstat;
- u32 next;
- u32 buff_ptr;
-} gt64240_td_t;
-
-typedef struct {
- u16 buff_sz; //-prk21aug01 u32 buff_sz:16;
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u32 cmdstat;
- u32 next;
- u32 buff_ptr;
-} gt64240_rd_t;
-#else
-#error Either __BIG_ENDIAN or __LITTLE_ENDIAN must be defined!
-#endif
-
-
-/* Values for the Tx command-status descriptor entry. */
-enum td_cmdstat {
- txOwn = 1 << 31,
- txAutoMode = 1 << 30,
- txEI = 1 << 23,
- txGenCRC = 1 << 22,
- txPad = 1 << 18,
- txFirst = 1 << 17,
- txLast = 1 << 16,
- txErrorSummary = 1 << 15,
- txReTxCntMask = 0x0f << 10,
- txReTxCntBit = 10,
- txCollision = 1 << 9,
- txReTxLimit = 1 << 8,
- txUnderrun = 1 << 6,
- txLateCollision = 1 << 5
-};
-
-
-/* Values for the Rx command-status descriptor entry. */
-enum rd_cmdstat {
- rxOwn = 1 << 31,
- rxAutoMode = 1 << 30,
- rxEI = 1 << 23,
- rxFirst = 1 << 17,
- rxLast = 1 << 16,
- rxErrorSummary = 1 << 15,
- rxIGMP = 1 << 14,
- rxHashExpired = 1 << 13,
- rxMissedFrame = 1 << 12,
- rxFrameType = 1 << 11,
- rxShortFrame = 1 << 8,
- rxMaxFrameLen = 1 << 7,
- rxOverrun = 1 << 6,
- rxCollision = 1 << 4,
- rxCRCError = 1
-};
-
-/* Bit fields of a Hash Table Entry */
-enum hash_table_entry {
- hteValid = 1,
- hteSkip = 2,
- hteRD = 4
-};
-
-// The MIB counters
-typedef struct {
- u32 byteReceived;
- u32 byteSent;
- u32 framesReceived;
- u32 framesSent;
- u32 totalByteReceived;
- u32 totalFramesReceived;
- u32 broadcastFramesReceived;
- u32 multicastFramesReceived;
- u32 cRCError;
- u32 oversizeFrames;
- u32 fragments;
- u32 jabber;
- u32 collision;
- u32 lateCollision;
- u32 frames64;
- u32 frames65_127;
- u32 frames128_255;
- u32 frames256_511;
- u32 frames512_1023;
- u32 frames1024_MaxSize;
- u32 macRxError;
- u32 droppedFrames;
- u32 outMulticastFrames;
- u32 outBroadcastFrames;
- u32 undersizeFrames;
-} mib_counters_t;
-
-
-struct gt64240_private {
- gt64240_rd_t *rx_ring;
- gt64240_td_t *tx_ring;
- // The Rx and Tx rings must be 16-byte aligned
- dma_addr_t rx_ring_dma;
- dma_addr_t tx_ring_dma;
- char *hash_table;
- // The Hash Table must be 8-byte aligned
- dma_addr_t hash_table_dma;
- int hash_mode;
-
- // The Rx buffers must be 8-byte aligned
- char *rx_buff;
- dma_addr_t rx_buff_dma;
- // Tx buffers (tx_skbuff[i]->data) with less than 8 bytes
- // of payload must be 8-byte aligned
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- int rx_next_out; /* The next free ring entry to receive */
- int tx_next_in; /* The next free ring entry to send */
- int tx_next_out; /* The last ring entry the ISR processed */
- int tx_count; /* current # of pkts waiting to be sent in Tx ring */
- int intr_work_done; /* number of Rx and Tx pkts processed in the isr */
- int tx_full; /* Tx ring is full */
-
- mib_counters_t mib;
- struct net_device_stats stats;
-
- int io_size;
- int port_num; // 0 or 1
- u32 port_offset;
-
- int phy_addr; // PHY address
- u32 last_psr; // last value of the port status register
-
- int options; /* User-settable misc. driver options. */
- int drv_flags;
- spinlock_t lock; /* Serialise access to device */
- struct mii_if_info mii_if;
-
- u32 msg_enable;
-};
-
-#endif /* _GT64240ETH_H */
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index e9e6d99a9ad..7c8ccc09b60 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -287,6 +287,7 @@ comment "FIR device drivers"
config USB_IRDA
tristate "IrDA USB dongles"
depends on IRDA && USB
+ select FW_LOADER
---help---
Say Y here if you want to build support for the USB IrDA FIR Dongle
device driver. To compile it as a module, choose M here: the module
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 2a0d538b387..383cef1f599 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -671,10 +671,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
* Jean II */
done = 1;
break;
- case -ECONNABORTED: /* -103 */
- case -ECONNRESET: /* -104 */
- case -ETIMEDOUT: /* -110 */
- case -ENOENT: /* -2 (urb unlinked by us) */
+ case -ECONNRESET:
+ case -ENOENT: /* urb unlinked by us */
default: /* ??? - Play safe */
urb->status = 0;
netif_wake_queue(self->netdev);
@@ -712,10 +710,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
* Jean II */
done = 1;
break;
- case -ECONNABORTED: /* -103 */
- case -ECONNRESET: /* -104 */
- case -ETIMEDOUT: /* -110 */
- case -ENOENT: /* -2 (urb unlinked by us) */
+ case -ECONNRESET:
+ case -ENOENT: /* urb unlinked by us */
default: /* ??? - Play safe */
if(skb != NULL) {
dev_kfree_skb_any(skb);
@@ -845,14 +841,14 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs)
self->stats.rx_crc_errors++;
/* Also precursor to a hot-unplug on UHCI. */
/* Fallthrough... */
- case -ECONNRESET: /* -104 */
+ case -ECONNRESET:
/* Random error, if I remember correctly */
/* uhci_cleanup_unlink() is going to kill the Rx
* URB just after we return. No problem, at this
* point the URB will be idle ;-) - Jean II */
- case -ESHUTDOWN: /* -108 */
+ case -ESHUTDOWN:
/* That's usually a hot-unplug. Submit will fail... */
- case -ETIMEDOUT: /* -110 */
+ case -ETIME:
/* Usually precursor to a hot-unplug on OHCI. */
default:
self->stats.rx_errors++;
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index cb62f2a9676..7185a4ee3c1 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -110,7 +110,7 @@ static nsc_chip_t chips[] = {
{ "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8,
nsc_ircc_probe_338, nsc_ircc_init_338 },
/* Contributed by Steffen Pingel - IBM X40 */
- { "PC8738x", { 0x164e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff,
+ { "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff,
nsc_ircc_probe_39x, nsc_ircc_init_39x },
/* Contributed by Jan Frey - IBM A30/A31 */
{ "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff,
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index 2eff45bedc7..22358ff68c4 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -2354,6 +2354,26 @@ static int __init smsc_superio_lpc(unsigned short cfg_base)
#define PCIID_VENDOR_INTEL 0x8086
#define PCIID_VENDOR_ALI 0x10b9
static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = {
+ /*
+ * Subsystems needing entries:
+ * 0x10b9:0x1533 0x103c:0x0850 HP nx9010 family
+ * 0x10b9:0x1533 0x0e11:0x005a Compaq nc4000 family
+ * 0x8086:0x24cc 0x0e11:0x002a HP nx9000 family
+ */
+ {
+ /* Guessed entry */
+ .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .device = 0x24cc,
+ .subvendor = 0x103c,
+ .subdevice = 0x08bc,
+ .sir_io = 0x02f8,
+ .fir_io = 0x0130,
+ .fir_irq = 0x05,
+ .fir_dma = 0x03,
+ .cfg_base = 0x004e,
+ .preconfigure = preconfigure_through_82801,
+ .name = "HP nx5000 family",
+ },
{
.vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
@@ -2366,7 +2386,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.fir_dma = 0x03,
.cfg_base = 0x004e,
.preconfigure = preconfigure_through_82801,
- .name = "HP nc8000",
+ .name = "HP nc8000 family",
},
{
.vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
@@ -2379,7 +2399,21 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.fir_dma = 0x03,
.cfg_base = 0x004e,
.preconfigure = preconfigure_through_82801,
- .name = "HP nc6000",
+ .name = "HP nc6000 family",
+ },
+ {
+ .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .device = 0x24cc,
+ .subvendor = 0x0e11,
+ .subdevice = 0x0860,
+ /* I assume these are the same for x1000 as for the others */
+ .sir_io = 0x02e8,
+ .fir_io = 0x02f8,
+ .fir_irq = 0x07,
+ .fir_dma = 0x03,
+ .cfg_base = 0x002e,
+ .preconfigure = preconfigure_through_82801,
+ .name = "Compaq x1000 family",
},
{
/* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index d61b208b52a..12103c93f7e 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -149,8 +149,6 @@ enum StirFifoCtlMask {
FIFOCTL_DIR = 0x10,
FIFOCTL_CLR = 0x08,
FIFOCTL_EMPTY = 0x04,
- FIFOCTL_RXERR = 0x02,
- FIFOCTL_TXERR = 0x01,
};
enum StirDiagMask {
@@ -615,19 +613,6 @@ static int fifo_txwait(struct stir_cb *stir, int space)
pr_debug("fifo status 0x%lx count %lu\n", status, count);
- /* error when receive/transmit fifo gets confused */
- if (status & FIFOCTL_RXERR) {
- stir->stats.rx_fifo_errors++;
- stir->stats.rx_errors++;
- break;
- }
-
- if (status & FIFOCTL_TXERR) {
- stir->stats.tx_fifo_errors++;
- stir->stats.tx_errors++;
- break;
- }
-
/* is fifo receiving already, or empty */
if (!(status & FIFOCTL_DIR)
|| (status & FIFOCTL_EMPTY))
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
index 79b85f32750..d916e1257c4 100644
--- a/drivers/net/irda/via-ircc.c
+++ b/drivers/net/irda/via-ircc.c
@@ -1223,8 +1223,13 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase)
IRDA_DEBUG(2, "%s(): len=%x\n", __FUNCTION__, len);
+ if ((len - 4) < 2) {
+ self->stats.rx_dropped++;
+ return FALSE;
+ }
+
skb = dev_alloc_skb(len + 1);
- if ((skb == NULL) || ((len - 4) < 2)) {
+ if (skb == NULL) {
self->stats.rx_dropped++;
return FALSE;
}
diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h
index a82a4ba8de4..c37f0bc4c7f 100644
--- a/drivers/net/irda/vlsi_ir.h
+++ b/drivers/net/irda/vlsi_ir.h
@@ -58,7 +58,7 @@ typedef void irqreturn_t;
/* PDE() introduced in 2.5.4 */
#ifdef CONFIG_PROC_FS
-#define PDE(inode) ((inode)->u.generic_ip)
+#define PDE(inode) ((inode)->i_private)
#endif
/* irda crc16 calculation exported in 2.5.42 */
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index f429b19bf62..4178b4b1d2d 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -161,15 +161,13 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
return(0);
}
+static struct net_device_stats loopback_stats;
+
static struct net_device_stats *get_stats(struct net_device *dev)
{
- struct net_device_stats *stats = dev->priv;
+ struct net_device_stats *stats = &loopback_stats;
int i;
- if (!stats) {
- return NULL;
- }
-
memset(stats, 0, sizeof(struct net_device_stats));
for_each_possible_cpu(i) {
@@ -185,19 +183,28 @@ static struct net_device_stats *get_stats(struct net_device *dev)
return stats;
}
-static u32 loopback_get_link(struct net_device *dev)
+static u32 always_on(struct net_device *dev)
{
return 1;
}
static const struct ethtool_ops loopback_ethtool_ops = {
- .get_link = loopback_get_link,
+ .get_link = always_on,
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
+ .get_tx_csum = always_on,
+ .get_sg = always_on,
+ .get_rx_csum = always_on,
};
+/*
+ * The loopback device is special. There is only one instance and
+ * it is statically allocated. Don't do this for other devices.
+ */
struct net_device loopback_dev = {
.name = "lo",
+ .get_stats = &get_stats,
+ .priv = &loopback_stats,
.mtu = (16 * 1024) + 20 + 20 + 12,
.hard_start_xmit = loopback_xmit,
.hard_header = eth_header,
@@ -221,16 +228,6 @@ struct net_device loopback_dev = {
/* Setup and register the loopback device. */
int __init loopback_init(void)
{
- struct net_device_stats *stats;
-
- /* Can survive without statistics */
- stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
- if (stats) {
- memset(stats, 0, sizeof(struct net_device_stats));
- loopback_dev.priv = stats;
- loopback_dev.get_stats = &get_stats;
- }
-
return register_netdev(&loopback_dev);
};
diff --git a/drivers/net/ne3210.c b/drivers/net/ne3210.c
index 0fa8e4d2276..d6632897542 100644
--- a/drivers/net/ne3210.c
+++ b/drivers/net/ne3210.c
@@ -343,6 +343,7 @@ static struct eisa_device_id ne3210_ids[] = {
{ "NVL1801" },
{ "" },
};
+MODULE_DEVICE_TABLE(eisa, ne3210_ids);
static struct eisa_driver ne3210_eisa_driver = {
.id_table = ne3210_ids,
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 2d1ecfdc80d..ecd3da151e2 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -522,7 +522,7 @@ EXPORT_SYMBOL(genphy_read_status);
static int genphy_config_init(struct phy_device *phydev)
{
- u32 val;
+ int val;
u32 features;
/* For now, I'll claim that the generic driver supports
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index 5666ed99814..0adee733b76 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -600,6 +600,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
po->chan.hdrlen = (sizeof(struct pppoe_hdr) +
dev->hard_header_len);
+ po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr);
po->chan.private = sk;
po->chan.ops = &pppoe_chan_ops;
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index f5dbeb27b6f..1bf23e41f58 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -2904,7 +2904,7 @@ static void s2io_mdio_write(u32 mmd_type, u64 addr, u16 value, struct net_device
{
u64 val64 = 0x0;
nic_t *sp = dev->priv;
- XENA_dev_config_t *bar0 = (XENA_dev_config_t *)sp->bar0;
+ XENA_dev_config_t __iomem *bar0 = sp->bar0;
//address transaction
val64 = val64 | MDIO_MMD_INDX_ADDR(addr)
@@ -2953,7 +2953,7 @@ static u64 s2io_mdio_read(u32 mmd_type, u64 addr, struct net_device *dev)
u64 val64 = 0x0;
u64 rval64 = 0x0;
nic_t *sp = dev->priv;
- XENA_dev_config_t *bar0 = (XENA_dev_config_t *)sp->bar0;
+ XENA_dev_config_t __iomem *bar0 = sp->bar0;
/* address transaction */
val64 = val64 | MDIO_MMD_INDX_ADDR(addr)
@@ -3276,7 +3276,7 @@ static void alarm_intr_handler(struct s2io_nic *nic)
* SUCCESS on success and FAILURE on failure.
*/
-static int wait_for_cmd_complete(void *addr, u64 busy_bit)
+static int wait_for_cmd_complete(void __iomem *addr, u64 busy_bit)
{
int ret = FAILURE, cnt = 0;
u64 val64;
@@ -4303,11 +4303,11 @@ static struct net_device_stats *s2io_get_stats(struct net_device *dev)
sp->stats.tx_errors =
le32_to_cpu(mac_control->stats_info->tmac_any_err_frms);
sp->stats.rx_errors =
- le32_to_cpu(mac_control->stats_info->rmac_drop_frms);
+ le64_to_cpu(mac_control->stats_info->rmac_drop_frms);
sp->stats.multicast =
le32_to_cpu(mac_control->stats_info->rmac_vld_mcst_frms);
sp->stats.rx_length_errors =
- le32_to_cpu(mac_control->stats_info->rmac_long_frms);
+ le64_to_cpu(mac_control->stats_info->rmac_long_frms);
return (&sp->stats);
}
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 9142d91355b..705e9a8fa30 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -58,6 +58,7 @@
#define TX_WATCHDOG (5 * HZ)
#define NAPI_WEIGHT 64
#define BLINK_MS 250
+#define LINK_HZ (HZ/2)
MODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver");
MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
@@ -605,7 +606,12 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
if (hw->chip_id == CHIP_ID_GENESIS) {
switch (mode) {
case LED_MODE_OFF:
- xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
+ if (hw->phy_type == SK_PHY_BCOM)
+ xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
+ else {
+ skge_write32(hw, SK_REG(port, TX_LED_VAL), 0);
+ skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_T_OFF);
+ }
skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
skge_write32(hw, SK_REG(port, RX_LED_VAL), 0);
skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_T_OFF);
@@ -625,8 +631,14 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
skge_write32(hw, SK_REG(port, RX_LED_VAL), 100);
skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
- xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
- break;
+ if (hw->phy_type == SK_PHY_BCOM)
+ xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
+ else {
+ skge_write8(hw, SK_REG(port, TX_LED_TST), LED_T_ON);
+ skge_write32(hw, SK_REG(port, TX_LED_VAL), 100);
+ skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_START);
+ }
+
}
} else {
switch (mode) {
@@ -879,6 +891,9 @@ static int __xm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val)
xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr);
*val = xm_read16(hw, port, XM_PHY_DATA);
+ if (hw->phy_type == SK_PHY_XMAC)
+ goto ready;
+
for (i = 0; i < PHY_RETRIES; i++) {
if (xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_RDY)
goto ready;
@@ -965,7 +980,8 @@ static void genesis_reset(struct skge_hw *hw, int port)
xm_write16(hw, port, XM_RX_CMD, 0); /* reset RX CMD Reg */
/* disable Broadcom PHY IRQ */
- xm_write16(hw, port, PHY_BCOM_INT_MASK, 0xffff);
+ if (hw->phy_type == SK_PHY_BCOM)
+ xm_write16(hw, port, PHY_BCOM_INT_MASK, 0xffff);
xm_outhash(hw, port, XM_HSM, zero);
}
@@ -1000,60 +1016,64 @@ static void bcom_check_link(struct skge_hw *hw, int port)
if (netif_carrier_ok(dev))
skge_link_down(skge);
- } else {
- if (skge->autoneg == AUTONEG_ENABLE &&
- (status & PHY_ST_AN_OVER)) {
- u16 lpa = xm_phy_read(hw, port, PHY_BCOM_AUNE_LP);
- u16 aux = xm_phy_read(hw, port, PHY_BCOM_AUX_STAT);
-
- if (lpa & PHY_B_AN_RF) {
- printk(KERN_NOTICE PFX "%s: remote fault\n",
- dev->name);
- return;
- }
+ return;
+ }
- /* Check Duplex mismatch */
- switch (aux & PHY_B_AS_AN_RES_MSK) {
- case PHY_B_RES_1000FD:
- skge->duplex = DUPLEX_FULL;
- break;
- case PHY_B_RES_1000HD:
- skge->duplex = DUPLEX_HALF;
- break;
- default:
- printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
- dev->name);
- return;
- }
+ if (skge->autoneg == AUTONEG_ENABLE) {
+ u16 lpa, aux;
+ if (!(status & PHY_ST_AN_OVER))
+ return;
- /* We are using IEEE 802.3z/D5.0 Table 37-4 */
- switch (aux & PHY_B_AS_PAUSE_MSK) {
- case PHY_B_AS_PAUSE_MSK:
- skge->flow_control = FLOW_MODE_SYMMETRIC;
- break;
- case PHY_B_AS_PRR:
- skge->flow_control = FLOW_MODE_REM_SEND;
- break;
- case PHY_B_AS_PRT:
- skge->flow_control = FLOW_MODE_LOC_SEND;
- break;
- default:
- skge->flow_control = FLOW_MODE_NONE;
- }
+ lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
+ if (lpa & PHY_B_AN_RF) {
+ printk(KERN_NOTICE PFX "%s: remote fault\n",
+ dev->name);
+ return;
+ }
- skge->speed = SPEED_1000;
+ aux = xm_phy_read(hw, port, PHY_BCOM_AUX_STAT);
+
+ /* Check Duplex mismatch */
+ switch (aux & PHY_B_AS_AN_RES_MSK) {
+ case PHY_B_RES_1000FD:
+ skge->duplex = DUPLEX_FULL;
+ break;
+ case PHY_B_RES_1000HD:
+ skge->duplex = DUPLEX_HALF;
+ break;
+ default:
+ printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
+ dev->name);
+ return;
}
- if (!netif_carrier_ok(dev))
- genesis_link_up(skge);
+
+ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
+ switch (aux & PHY_B_AS_PAUSE_MSK) {
+ case PHY_B_AS_PAUSE_MSK:
+ skge->flow_control = FLOW_MODE_SYMMETRIC;
+ break;
+ case PHY_B_AS_PRR:
+ skge->flow_control = FLOW_MODE_REM_SEND;
+ break;
+ case PHY_B_AS_PRT:
+ skge->flow_control = FLOW_MODE_LOC_SEND;
+ break;
+ default:
+ skge->flow_control = FLOW_MODE_NONE;
+ }
+ skge->speed = SPEED_1000;
}
+
+ if (!netif_carrier_ok(dev))
+ genesis_link_up(skge);
}
/* Broadcom 5400 only supports giagabit! SysKonnect did not put an additional
* Phy on for 100 or 10Mbit operation
*/
-static void bcom_phy_init(struct skge_port *skge, int jumbo)
+static void bcom_phy_init(struct skge_port *skge)
{
struct skge_hw *hw = skge->hw;
int port = skge->port;
@@ -1144,7 +1164,7 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
phy_pause_map[skge->flow_control] | PHY_AN_CSMA);
/* Handle Jumbo frames */
- if (jumbo) {
+ if (hw->dev[port]->mtu > ETH_DATA_LEN) {
xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
PHY_B_AC_TX_TST | PHY_B_AC_LONG_PACK);
@@ -1157,8 +1177,154 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
/* Use link status change interrupt */
xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+}
- bcom_check_link(hw, port);
+static void xm_phy_init(struct skge_port *skge)
+{
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+ u16 ctrl = 0;
+
+ if (skge->autoneg == AUTONEG_ENABLE) {
+ if (skge->advertising & ADVERTISED_1000baseT_Half)
+ ctrl |= PHY_X_AN_HD;
+ if (skge->advertising & ADVERTISED_1000baseT_Full)
+ ctrl |= PHY_X_AN_FD;
+
+ switch(skge->flow_control) {
+ case FLOW_MODE_NONE:
+ ctrl |= PHY_X_P_NO_PAUSE;
+ break;
+ case FLOW_MODE_LOC_SEND:
+ ctrl |= PHY_X_P_ASYM_MD;
+ break;
+ case FLOW_MODE_SYMMETRIC:
+ ctrl |= PHY_X_P_BOTH_MD;
+ break;
+ }
+
+ xm_phy_write(hw, port, PHY_XMAC_AUNE_ADV, ctrl);
+
+ /* Restart Auto-negotiation */
+ ctrl = PHY_CT_ANE | PHY_CT_RE_CFG;
+ } else {
+ /* Set DuplexMode in Config register */
+ if (skge->duplex == DUPLEX_FULL)
+ ctrl |= PHY_CT_DUP_MD;
+ /*
+ * Do NOT enable Auto-negotiation here. This would hold
+ * the link down because no IDLEs are transmitted
+ */
+ }
+
+ xm_phy_write(hw, port, PHY_XMAC_CTRL, ctrl);
+
+ /* Poll PHY for status changes */
+ schedule_delayed_work(&skge->link_thread, LINK_HZ);
+}
+
+static void xm_check_link(struct net_device *dev)
+{
+ struct skge_port *skge = netdev_priv(dev);
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+ u16 status;
+
+ /* read twice because of latch */
+ (void) xm_phy_read(hw, port, PHY_XMAC_STAT);
+ status = xm_phy_read(hw, port, PHY_XMAC_STAT);
+
+ if ((status & PHY_ST_LSYNC) == 0) {
+ u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
+ cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
+ xm_write16(hw, port, XM_MMU_CMD, cmd);
+ /* dummy read to ensure writing */
+ (void) xm_read16(hw, port, XM_MMU_CMD);
+
+ if (netif_carrier_ok(dev))
+ skge_link_down(skge);
+ return;
+ }
+
+ if (skge->autoneg == AUTONEG_ENABLE) {
+ u16 lpa, res;
+
+ if (!(status & PHY_ST_AN_OVER))
+ return;
+
+ lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
+ if (lpa & PHY_B_AN_RF) {
+ printk(KERN_NOTICE PFX "%s: remote fault\n",
+ dev->name);
+ return;
+ }
+
+ res = xm_phy_read(hw, port, PHY_XMAC_RES_ABI);
+
+ /* Check Duplex mismatch */
+ switch (res & (PHY_X_RS_HD | PHY_X_RS_FD)) {
+ case PHY_X_RS_FD:
+ skge->duplex = DUPLEX_FULL;
+ break;
+ case PHY_X_RS_HD:
+ skge->duplex = DUPLEX_HALF;
+ break;
+ default:
+ printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
+ dev->name);
+ return;
+ }
+
+ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
+ if (lpa & PHY_X_P_SYM_MD)
+ skge->flow_control = FLOW_MODE_SYMMETRIC;
+ else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_ASYM_MD)
+ skge->flow_control = FLOW_MODE_REM_SEND;
+ else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_BOTH_MD)
+ skge->flow_control = FLOW_MODE_LOC_SEND;
+ else
+ skge->flow_control = FLOW_MODE_NONE;
+
+
+ skge->speed = SPEED_1000;
+ }
+
+ if (!netif_carrier_ok(dev))
+ genesis_link_up(skge);
+}
+
+/* Poll to check for link coming up.
+ * Since internal PHY is wired to a level triggered pin, can't
+ * get an interrupt when carrier is detected.
+ */
+static void xm_link_timer(void *arg)
+{
+ struct net_device *dev = arg;
+ struct skge_port *skge = netdev_priv(arg);
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+
+ if (!netif_running(dev))
+ return;
+
+ if (netif_carrier_ok(dev)) {
+ xm_read16(hw, port, XM_ISRC);
+ if (!(xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS))
+ goto nochange;
+ } else {
+ if (xm_read32(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
+ goto nochange;
+ xm_read16(hw, port, XM_ISRC);
+ if (xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS)
+ goto nochange;
+ }
+
+ mutex_lock(&hw->phy_mutex);
+ xm_check_link(dev);
+ mutex_unlock(&hw->phy_mutex);
+
+nochange:
+ schedule_delayed_work(&skge->link_thread, LINK_HZ);
}
static void genesis_mac_init(struct skge_hw *hw, int port)
@@ -1189,20 +1355,29 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
* namely for the 1000baseTX cards that use the XMAC's
* GMII mode.
*/
- /* Take external Phy out of reset */
- r = skge_read32(hw, B2_GP_IO);
- if (port == 0)
- r |= GP_DIR_0|GP_IO_0;
- else
- r |= GP_DIR_2|GP_IO_2;
+ if (hw->phy_type != SK_PHY_XMAC) {
+ /* Take external Phy out of reset */
+ r = skge_read32(hw, B2_GP_IO);
+ if (port == 0)
+ r |= GP_DIR_0|GP_IO_0;
+ else
+ r |= GP_DIR_2|GP_IO_2;
- skge_write32(hw, B2_GP_IO, r);
+ skge_write32(hw, B2_GP_IO, r);
+ /* Enable GMII interface */
+ xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
+ }
- /* Enable GMII interface */
- xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
- bcom_phy_init(skge, jumbo);
+ switch(hw->phy_type) {
+ case SK_PHY_XMAC:
+ xm_phy_init(skge);
+ break;
+ case SK_PHY_BCOM:
+ bcom_phy_init(skge);
+ bcom_check_link(hw, port);
+ }
/* Set Station Address */
xm_outaddr(hw, port, XM_SA, dev->dev_addr);
@@ -1335,16 +1510,18 @@ static void genesis_stop(struct skge_port *skge)
skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_SET_MAC_RST);
/* For external PHYs there must be special handling */
- reg = skge_read32(hw, B2_GP_IO);
- if (port == 0) {
- reg |= GP_DIR_0;
- reg &= ~GP_IO_0;
- } else {
- reg |= GP_DIR_2;
- reg &= ~GP_IO_2;
+ if (hw->phy_type != SK_PHY_XMAC) {
+ reg = skge_read32(hw, B2_GP_IO);
+ if (port == 0) {
+ reg |= GP_DIR_0;
+ reg &= ~GP_IO_0;
+ } else {
+ reg |= GP_DIR_2;
+ reg &= ~GP_IO_2;
+ }
+ skge_write32(hw, B2_GP_IO, reg);
+ skge_read32(hw, B2_GP_IO);
}
- skge_write32(hw, B2_GP_IO, reg);
- skge_read32(hw, B2_GP_IO);
xm_write16(hw, port, XM_MMU_CMD,
xm_read16(hw, port, XM_MMU_CMD)
@@ -1406,7 +1583,7 @@ static void genesis_link_up(struct skge_port *skge)
struct skge_hw *hw = skge->hw;
int port = skge->port;
u16 cmd;
- u32 mode, msk;
+ u32 mode;
cmd = xm_read16(hw, port, XM_MMU_CMD);
@@ -1454,27 +1631,24 @@ static void genesis_link_up(struct skge_port *skge)
}
xm_write32(hw, port, XM_MODE, mode);
-
- msk = XM_DEF_MSK;
- /* disable GP0 interrupt bit for external Phy */
- msk |= XM_IS_INP_ASS;
-
- xm_write16(hw, port, XM_IMSK, msk);
+ xm_write16(hw, port, XM_IMSK, XM_DEF_MSK);
xm_read16(hw, port, XM_ISRC);
/* get MMU Command Reg. */
cmd = xm_read16(hw, port, XM_MMU_CMD);
- if (skge->duplex == DUPLEX_FULL)
+ if (hw->phy_type != SK_PHY_XMAC && skge->duplex == DUPLEX_FULL)
cmd |= XM_MMU_GMII_FD;
/*
* Workaround BCOM Errata (#10523) for all BCom Phys
* Enable Power Management after link up
*/
- xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
- xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL)
- & ~PHY_B_AC_DIS_PM);
- xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+ if (hw->phy_type == SK_PHY_BCOM) {
+ xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
+ xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL)
+ & ~PHY_B_AC_DIS_PM);
+ xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+ }
/* enable Rx/Tx */
xm_write16(hw, port, XM_MMU_CMD,
@@ -2240,6 +2414,8 @@ static int skge_down(struct net_device *dev)
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
netif_stop_queue(dev);
+ if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)
+ cancel_rearming_delayed_work(&skge->link_thread);
skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
if (hw->chip_id == CHIP_ID_GENESIS)
@@ -2862,7 +3038,7 @@ static void skge_extirq(void *arg)
if (netif_running(dev)) {
if (hw->chip_id != CHIP_ID_GENESIS)
yukon_phy_intr(skge);
- else
+ else if (hw->phy_type == SK_PHY_BCOM)
bcom_phy_intr(skge);
}
}
@@ -3014,7 +3190,7 @@ static int skge_reset(struct skge_hw *hw)
{
u32 reg;
u16 ctst, pci_status;
- u8 t8, mac_cfg, pmd_type, phy_type;
+ u8 t8, mac_cfg, pmd_type;
int i;
ctst = skge_read16(hw, B0_CTST);
@@ -3038,19 +3214,22 @@ static int skge_reset(struct skge_hw *hw)
ctst & (CS_CLK_RUN_HOT|CS_CLK_RUN_RST|CS_CLK_RUN_ENA));
hw->chip_id = skge_read8(hw, B2_CHIP_ID);
- phy_type = skge_read8(hw, B2_E_1) & 0xf;
+ hw->phy_type = skge_read8(hw, B2_E_1) & 0xf;
pmd_type = skge_read8(hw, B2_PMD_TYP);
hw->copper = (pmd_type == 'T' || pmd_type == '1');
switch (hw->chip_id) {
case CHIP_ID_GENESIS:
- switch (phy_type) {
+ switch (hw->phy_type) {
+ case SK_PHY_XMAC:
+ hw->phy_addr = PHY_ADDR_XMAC;
+ break;
case SK_PHY_BCOM:
hw->phy_addr = PHY_ADDR_BCOM;
break;
default:
printk(KERN_ERR PFX "%s: unsupported phy type 0x%x\n",
- pci_name(hw->pdev), phy_type);
+ pci_name(hw->pdev), hw->phy_type);
return -EOPNOTSUPP;
}
break;
@@ -3058,7 +3237,7 @@ static int skge_reset(struct skge_hw *hw)
case CHIP_ID_YUKON:
case CHIP_ID_YUKON_LITE:
case CHIP_ID_YUKON_LP:
- if (phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
+ if (hw->phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
hw->copper = 1;
hw->phy_addr = PHY_ADDR_MARV;
@@ -3089,10 +3268,13 @@ static int skge_reset(struct skge_hw *hw)
else
hw->ram_size = t8 * 4096;
- hw->intr_mask = IS_HW_ERR | IS_EXT_REG | IS_PORT_1;
+ hw->intr_mask = IS_HW_ERR | IS_PORT_1;
if (hw->ports > 1)
hw->intr_mask |= IS_PORT_2;
+ if (!(hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC))
+ hw->intr_mask |= IS_EXT_REG;
+
if (hw->chip_id == CHIP_ID_GENESIS)
genesis_init(hw);
else {
@@ -3226,6 +3408,9 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
skge->port = port;
+ /* Only used for Genesis XMAC */
+ INIT_WORK(&skge->link_thread, xm_link_timer, dev);
+
if (hw->chip_id != CHIP_ID_GENESIS) {
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
skge->rx_csum = 1;
diff --git a/drivers/net/skge.h b/drivers/net/skge.h
index 79e09271bcf..d0b47d46cf9 100644
--- a/drivers/net/skge.h
+++ b/drivers/net/skge.h
@@ -934,7 +934,7 @@ enum {
PHY_XMAC_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */
PHY_XMAC_AUNE_LP = 0x05,/* 16 bit r/o Link Partner Abi Reg */
PHY_XMAC_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */
- PHY_XMAC_NEPG = 0x07,/* 16 bit r/w Next Page Register */
+ PHY_XMAC_NEPG = 0x07,/* 16 bit r/w Next Page Register */
PHY_XMAC_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner */
PHY_XMAC_EXT_STAT = 0x0f,/* 16 bit r/o Ext Status Register */
@@ -1097,13 +1097,36 @@ enum {
/* Pause Bits (PHY_X_AN_PAUSE and PHY_X_RS_PAUSE) encoding */
enum {
- PHY_X_P_NO_PAUSE = 0<<7,/* Bit 8..7: no Pause Mode */
+ PHY_X_P_NO_PAUSE= 0<<7,/* Bit 8..7: no Pause Mode */
PHY_X_P_SYM_MD = 1<<7, /* Bit 8..7: symmetric Pause Mode */
PHY_X_P_ASYM_MD = 2<<7,/* Bit 8..7: asymmetric Pause Mode */
PHY_X_P_BOTH_MD = 3<<7,/* Bit 8..7: both Pause Mode */
};
+/***** PHY_XMAC_EXT_STAT 16 bit r/w Extended Status Register *****/
+enum {
+ PHY_X_EX_FD = 1<<15, /* Bit 15: Device Supports Full Duplex */
+ PHY_X_EX_HD = 1<<14, /* Bit 14: Device Supports Half Duplex */
+};
+
+/***** PHY_XMAC_RES_ABI 16 bit r/o PHY Resolved Ability *****/
+enum {
+ PHY_X_RS_PAUSE = 3<<7, /* Bit 8..7: selected Pause Mode */
+ PHY_X_RS_HD = 1<<6, /* Bit 6: Half Duplex Mode selected */
+ PHY_X_RS_FD = 1<<5, /* Bit 5: Full Duplex Mode selected */
+ PHY_X_RS_ABLMIS = 1<<4, /* Bit 4: duplex or pause cap mismatch */
+ PHY_X_RS_PAUMIS = 1<<3, /* Bit 3: pause capability mismatch */
+};
+
+/* Remote Fault Bits (PHY_X_AN_RFB) encoding */
+enum {
+ X_RFB_OK = 0<<12,/* Bit 13..12 No errors, Link OK */
+ X_RFB_LF = 1<<12,/* Bit 13..12 Link Failure */
+ X_RFB_OFF = 2<<12,/* Bit 13..12 Offline */
+ X_RFB_AN_ERR = 3<<12,/* Bit 13..12 Auto-Negotiation Error */
+};
+
/* Broadcom-Specific */
/***** PHY_BCOM_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/
enum {
@@ -2158,8 +2181,8 @@ enum {
XM_IS_LNK_AE = 1<<14, /* Bit 14: Link Asynchronous Event */
XM_IS_TX_ABORT = 1<<13, /* Bit 13: Transmit Abort, late Col. etc */
XM_IS_FRC_INT = 1<<12, /* Bit 12: Force INT bit set in GP */
- XM_IS_INP_ASS = 1<<11, /* Bit 11: Input Asserted, GP bit 0 set */
- XM_IS_LIPA_RC = 1<<10, /* Bit 10: Link Partner requests config */
+ XM_IS_INP_ASS = 1<<11, /* Bit 11: Input Asserted, GP bit 0 set */
+ XM_IS_LIPA_RC = 1<<10, /* Bit 10: Link Partner requests config */
XM_IS_RX_PAGE = 1<<9, /* Bit 9: Page Received */
XM_IS_TX_PAGE = 1<<8, /* Bit 8: Next Page Loaded for Transmit */
XM_IS_AND = 1<<7, /* Bit 7: Auto-Negotiation Done */
@@ -2172,9 +2195,7 @@ enum {
XM_IS_RX_COMP = 1<<0, /* Bit 0: Frame Rx Complete */
};
-#define XM_DEF_MSK (~(XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE | \
- XM_IS_AND | XM_IS_RXC_OV | XM_IS_TXC_OV | \
- XM_IS_RXF_OV | XM_IS_TXF_UR))
+#define XM_DEF_MSK (~(XM_IS_RXC_OV | XM_IS_TXC_OV | XM_IS_RXF_OV | XM_IS_TXF_UR))
/* XM_HW_CFG 16 bit r/w Hardware Config Register */
@@ -2396,6 +2417,7 @@ struct skge_hw {
u8 chip_rev;
u8 copper;
u8 ports;
+ u8 phy_type;
u32 ram_size;
u32 ram_offset;
@@ -2422,6 +2444,7 @@ struct skge_port {
struct net_device_stats net_stats;
+ struct work_struct link_thread;
u8 rx_csum;
u8 blink_on;
u8 flow_control;
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 7aa7fbac822..fedd1a37bc3 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -195,6 +195,7 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg)
#define SMC_IRQ_FLAGS (( \
machine_is_omap_h2() \
|| machine_is_omap_h3() \
+ || machine_is_omap_h4() \
|| (machine_is_omap_innovator() && !cpu_is_omap1510()) \
) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING)
@@ -379,6 +380,24 @@ static inline void LPD7_SMC_outsw (unsigned char* a, int r,
#define SMC_IRQ_FLAGS (0)
+#elif defined(CONFIG_ARCH_VERSATILE)
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_NOWAIT 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS (0)
+
#else
#define SMC_CAN_USE_8BIT 1
diff --git a/drivers/net/stnic.c b/drivers/net/stnic.c
index 3fd7a4fee66..e6f90427160 100644
--- a/drivers/net/stnic.c
+++ b/drivers/net/stnic.c
@@ -19,7 +19,7 @@
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/se/se.h>
+#include <asm/se.h>
#include <asm/machvec.h>
#ifdef CONFIG_SH_STANDARD_BIOS
#include <asm/sh_bios.h>
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index aaf45b907a7..c25ba273b74 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -68,8 +68,8 @@
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "3.65"
-#define DRV_MODULE_RELDATE "August 07, 2006"
+#define DRV_MODULE_VERSION "3.66"
+#define DRV_MODULE_RELDATE "September 23, 2006"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -173,6 +173,7 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5722)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M)},
@@ -187,6 +188,7 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754M)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755M)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5756)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5786)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787M)},
@@ -197,6 +199,8 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780S)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5781)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906M)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)},
{PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)},
@@ -424,6 +428,16 @@ static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
readl(mbox);
}
+static u32 tg3_read32_mbox_5906(struct tg3 *tp, u32 off)
+{
+ return (readl(tp->regs + off + GRCMBOX_BASE));
+}
+
+static void tg3_write32_mbox_5906(struct tg3 *tp, u32 off, u32 val)
+{
+ writel(val, tp->regs + off + GRCMBOX_BASE);
+}
+
#define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val)
#define tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val))
#define tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val)
@@ -439,6 +453,10 @@ static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
{
unsigned long flags;
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) &&
+ (off >= NIC_SRAM_STATS_BLK) && (off < NIC_SRAM_TX_BUFFER_DESC))
+ return;
+
spin_lock_irqsave(&tp->indirect_lock, flags);
if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) {
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
@@ -460,6 +478,12 @@ static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
{
unsigned long flags;
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) &&
+ (off >= NIC_SRAM_STATS_BLK) && (off < NIC_SRAM_TX_BUFFER_DESC)) {
+ *val = 0;
+ return;
+ }
+
spin_lock_irqsave(&tp->indirect_lock, flags);
if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) {
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
@@ -489,6 +513,9 @@ static inline void tg3_cond_int(struct tg3 *tp)
if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) &&
(tp->hw_status->status & SD_STATUS_UPDATED))
tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_SETINT);
+ else
+ tw32(HOSTCC_MODE, tp->coalesce_mode |
+ (HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW));
}
static void tg3_enable_ints(struct tg3 *tp)
@@ -654,6 +681,10 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
unsigned int loops;
int ret;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 &&
+ (reg == MII_TG3_CTRL || reg == MII_TG3_AUX_CTRL))
+ return 0;
+
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
tw32_f(MAC_MI_MODE,
(tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
@@ -1004,6 +1035,24 @@ out:
phy_reg | MII_TG3_EXT_CTRL_FIFO_ELASTIC);
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 phy_reg;
+
+ /* adjust output voltage */
+ tg3_writephy(tp, MII_TG3_EPHY_PTEST, 0x12);
+
+ if (!tg3_readphy(tp, MII_TG3_EPHY_TEST, &phy_reg)) {
+ u32 phy_reg2;
+
+ tg3_writephy(tp, MII_TG3_EPHY_TEST,
+ phy_reg | MII_TG3_EPHY_SHADOW_EN);
+ /* Enable auto-MDIX */
+ if (!tg3_readphy(tp, 0x10, &phy_reg2))
+ tg3_writephy(tp, 0x10, phy_reg2 | 0x4000);
+ tg3_writephy(tp, MII_TG3_EPHY_TEST, phy_reg);
+ }
+ }
+
tg3_phy_set_wirespeed(tp);
return 0;
}
@@ -1117,6 +1166,15 @@ static void tg3_nvram_unlock(struct tg3 *);
static void tg3_power_down_phy(struct tg3 *tp)
{
+ if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)
+ return;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) {
+ tg3_writephy(tp, MII_TG3_EXT_CTRL,
+ MII_TG3_EXT_CTRL_FORCE_LED_OFF);
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
+ }
+
/* The PHY should not be powered down on some chips because
* of bugs.
*/
@@ -1199,7 +1257,12 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
tg3_setup_phy(tp, 0);
}
- if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 val;
+
+ val = tr32(GRC_VCPU_EXT_CTRL);
+ tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_DISABLE_WOL);
+ } else if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
int i;
u32 val;
@@ -1223,7 +1286,10 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a);
udelay(40);
- mac_mode = MAC_MODE_PORT_MODE_MII;
+ if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES)
+ mac_mode = MAC_MODE_PORT_MODE_GMII;
+ else
+ mac_mode = MAC_MODE_PORT_MODE_MII;
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 ||
!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB))
@@ -1301,15 +1367,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
}
if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) &&
- !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
- /* Turn off the PHY */
- if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
- tg3_writephy(tp, MII_TG3_EXT_CTRL,
- MII_TG3_EXT_CTRL_FORCE_LED_OFF);
- tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
- tg3_power_down_phy(tp);
- }
- }
+ !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))
+ tg3_power_down_phy(tp);
tg3_frob_aux_power(tp);
@@ -1467,6 +1526,13 @@ static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8
break;
default:
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ *speed = (val & MII_TG3_AUX_STAT_100) ? SPEED_100 :
+ SPEED_10;
+ *duplex = (val & MII_TG3_AUX_STAT_FULL) ? DUPLEX_FULL :
+ DUPLEX_HALF;
+ break;
+ }
*speed = SPEED_INVALID;
*duplex = DUPLEX_INVALID;
break;
@@ -1749,7 +1815,7 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT)
tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG);
- else
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)
tg3_writephy(tp, MII_TG3_IMASK, ~0);
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
@@ -2406,24 +2472,27 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
expected_sg_dig_ctrl |= (1 << 12);
if (sg_dig_ctrl != expected_sg_dig_ctrl) {
+ if ((tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT) &&
+ tp->serdes_counter &&
+ ((mac_status & (MAC_STATUS_PCS_SYNCED |
+ MAC_STATUS_RCVD_CFG)) ==
+ MAC_STATUS_PCS_SYNCED)) {
+ tp->serdes_counter--;
+ current_link_up = 1;
+ goto out;
+ }
+restart_autoneg:
if (workaround)
tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000);
tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30));
udelay(5);
tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl);
- tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter = SERDES_AN_TIMEOUT_5704S;
+ tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
} else if (mac_status & (MAC_STATUS_PCS_SYNCED |
MAC_STATUS_SIGNAL_DET)) {
- int i;
-
- /* Giver time to negotiate (~200ms) */
- for (i = 0; i < 40000; i++) {
- sg_dig_status = tr32(SG_DIG_STATUS);
- if (sg_dig_status & (0x3))
- break;
- udelay(5);
- }
+ sg_dig_status = tr32(SG_DIG_STATUS);
mac_status = tr32(MAC_STATUS);
if ((sg_dig_status & (1 << 1)) &&
@@ -2439,10 +2508,11 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
tg3_setup_flow_control(tp, local_adv, remote_adv);
current_link_up = 1;
- tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter = 0;
+ tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
} else if (!(sg_dig_status & (1 << 1))) {
- if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED)
- tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+ if (tp->serdes_counter)
+ tp->serdes_counter--;
else {
if (workaround) {
u32 val = serdes_cfg;
@@ -2466,9 +2536,17 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
!(mac_status & MAC_STATUS_RCVD_CFG)) {
tg3_setup_flow_control(tp, 0, 0);
current_link_up = 1;
- }
+ tp->tg3_flags2 |=
+ TG3_FLG2_PARALLEL_DETECT;
+ tp->serdes_counter =
+ SERDES_PARALLEL_DET_TIMEOUT;
+ } else
+ goto restart_autoneg;
}
}
+ } else {
+ tp->serdes_counter = SERDES_AN_TIMEOUT_5704S;
+ tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
}
out:
@@ -2599,14 +2677,16 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
MAC_STATUS_CFG_CHANGED));
udelay(5);
if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED |
- MAC_STATUS_CFG_CHANGED)) == 0)
+ MAC_STATUS_CFG_CHANGED |
+ MAC_STATUS_LNKSTATE_CHANGED)) == 0)
break;
}
mac_status = tr32(MAC_STATUS);
if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) {
current_link_up = 0;
- if (tp->link_config.autoneg == AUTONEG_ENABLE) {
+ if (tp->link_config.autoneg == AUTONEG_ENABLE &&
+ tp->serdes_counter == 0) {
tw32_f(MAC_MODE, (tp->mac_mode |
MAC_MODE_SEND_CONFIGS));
udelay(1);
@@ -2711,7 +2791,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
tg3_writephy(tp, MII_BMCR, bmcr);
tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED);
- tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter = SERDES_AN_TIMEOUT_5714S;
tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
return err;
@@ -2816,9 +2896,9 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
static void tg3_serdes_parallel_detect(struct tg3 *tp)
{
- if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) {
+ if (tp->serdes_counter) {
/* Give autoneg time to complete. */
- tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter--;
return;
}
if (!netif_carrier_ok(tp->dev) &&
@@ -3535,8 +3615,7 @@ static irqreturn_t tg3_test_isr(int irq, void *dev_id,
if ((sblk->status & SD_STATUS_UPDATED) ||
!(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) {
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
- 0x00000001);
+ tg3_disable_ints(tp);
return IRQ_RETVAL(1);
}
return IRQ_RETVAL(0);
@@ -4644,6 +4723,44 @@ static void tg3_write_sig_legacy(struct tg3 *tp, int kind)
}
}
+static int tg3_poll_fw(struct tg3 *tp)
+{
+ int i;
+ u32 val;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ for (i = 0; i < 400; i++) {
+ if (tr32(VCPU_STATUS) & VCPU_STATUS_INIT_DONE)
+ return 0;
+ udelay(10);
+ }
+ return -ENODEV;
+ }
+
+ /* Wait for firmware initialization to complete. */
+ for (i = 0; i < 100000; i++) {
+ tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val);
+ if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
+ break;
+ udelay(10);
+ }
+
+ /* Chip might not be fitted with firmware. Some Sun onboard
+ * parts are configured like that. So don't signal the timeout
+ * of the above loop as an error, but do report the lack of
+ * running firmware once.
+ */
+ if (i >= 100000 &&
+ !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) {
+ tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED;
+
+ printk(KERN_INFO PFX "%s: No firmware running.\n",
+ tp->dev->name);
+ }
+
+ return 0;
+}
+
static void tg3_stop_fw(struct tg3 *);
/* tp->lock is held. */
@@ -4651,7 +4768,7 @@ static int tg3_chip_reset(struct tg3 *tp)
{
u32 val;
void (*write_op)(struct tg3 *, u32, u32);
- int i;
+ int err;
tg3_nvram_lock(tp);
@@ -4688,6 +4805,12 @@ static int tg3_chip_reset(struct tg3 *tp)
}
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ tw32(VCPU_STATUS, tr32(VCPU_STATUS) | VCPU_STATUS_DRV_RESET);
+ tw32(GRC_VCPU_EXT_CTRL,
+ tr32(GRC_VCPU_EXT_CTRL) & ~GRC_VCPU_EXT_CTRL_HALT_CPU);
+ }
+
if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)
val |= GRC_MISC_CFG_KEEP_GPHY_POWER;
tw32(GRC_MISC_CFG, val);
@@ -4811,26 +4934,9 @@ static int tg3_chip_reset(struct tg3 *tp)
tw32_f(MAC_MODE, 0);
udelay(40);
- /* Wait for firmware initialization to complete. */
- for (i = 0; i < 100000; i++) {
- tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val);
- if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
- break;
- udelay(10);
- }
-
- /* Chip might not be fitted with firmare. Some Sun onboard
- * parts are configured like that. So don't signal the timeout
- * of the above loop as an error, but do report the lack of
- * running firmware once.
- */
- if (i >= 100000 &&
- !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) {
- tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED;
-
- printk(KERN_INFO PFX "%s: No firmware running.\n",
- tp->dev->name);
- }
+ err = tg3_poll_fw(tp);
+ if (err)
+ return err;
if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) &&
tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) {
@@ -5036,6 +5142,12 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
BUG_ON(offset == TX_CPU_BASE &&
(tp->tg3_flags2 & TG3_FLG2_5705_PLUS));
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 val = tr32(GRC_VCPU_EXT_CTRL);
+
+ tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU);
+ return 0;
+ }
if (offset == RX_CPU_BASE) {
for (i = 0; i < 10000; i++) {
tw32(offset + CPU_STATE, 0xffffffff);
@@ -6040,6 +6152,13 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
val = 1;
else if (val > tp->rx_std_max_post)
val = tp->rx_std_max_post;
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5906_A1)
+ tw32(ISO_PKT_TX, (tr32(ISO_PKT_TX) & ~0x3) | 0x2);
+
+ if (val > (TG3_RX_INTERNAL_RING_SZ_5906 / 2))
+ val = TG3_RX_INTERNAL_RING_SZ_5906 / 2;
+ }
tw32(RCVBDI_STD_THRESH, val);
@@ -6460,7 +6579,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
if (err)
return err;
- if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
+ if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) {
u32 tmp;
/* Clear CRC stats. */
@@ -6660,12 +6780,14 @@ static void tg3_timer(unsigned long __opaque)
need_setup = 1;
}
if (need_setup) {
- tw32_f(MAC_MODE,
- (tp->mac_mode &
- ~MAC_MODE_PORT_MODE_MASK));
- udelay(40);
- tw32_f(MAC_MODE, tp->mac_mode);
- udelay(40);
+ if (!tp->serdes_counter) {
+ tw32_f(MAC_MODE,
+ (tp->mac_mode &
+ ~MAC_MODE_PORT_MODE_MASK));
+ udelay(40);
+ tw32_f(MAC_MODE, tp->mac_mode);
+ udelay(40);
+ }
tg3_setup_phy(tp, 0);
}
} else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES)
@@ -6674,13 +6796,29 @@ static void tg3_timer(unsigned long __opaque)
tp->timer_counter = tp->timer_multiplier;
}
- /* Heartbeat is only sent once every 2 seconds. */
+ /* Heartbeat is only sent once every 2 seconds.
+ *
+ * The heartbeat is to tell the ASF firmware that the host
+ * driver is still alive. In the event that the OS crashes,
+ * ASF needs to reset the hardware to free up the FIFO space
+ * that may be filled with rx packets destined for the host.
+ * If the FIFO is full, ASF will no longer function properly.
+ *
+ * Unintended resets have been reported on real time kernels
+ * where the timer doesn't run on time. Netpoll will also have
+ * same problem.
+ *
+ * The new FWCMD_NICDRV_ALIVE3 command tells the ASF firmware
+ * to check the ring condition when the heartbeat is expiring
+ * before doing the reset. This will prevent most unintended
+ * resets.
+ */
if (!--tp->asf_counter) {
if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
u32 val;
tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX,
- FWCMD_NICDRV_ALIVE2);
+ FWCMD_NICDRV_ALIVE3);
tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
/* 5 seconds timeout */
tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5);
@@ -6721,8 +6859,7 @@ static int tg3_request_irq(struct tg3 *tp)
static int tg3_test_interrupt(struct tg3 *tp)
{
struct net_device *dev = tp->dev;
- int err, i;
- u32 int_mbox = 0;
+ int err, i, intr_ok = 0;
if (!netif_running(dev))
return -ENODEV;
@@ -6743,10 +6880,18 @@ static int tg3_test_interrupt(struct tg3 *tp)
HOSTCC_MODE_NOW);
for (i = 0; i < 5; i++) {
+ u32 int_mbox, misc_host_ctrl;
+
int_mbox = tr32_mailbox(MAILBOX_INTERRUPT_0 +
TG3_64BIT_REG_LOW);
- if (int_mbox != 0)
+ misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL);
+
+ if ((int_mbox != 0) ||
+ (misc_host_ctrl & MISC_HOST_CTRL_MASK_PCI_INT)) {
+ intr_ok = 1;
break;
+ }
+
msleep(10);
}
@@ -6759,7 +6904,7 @@ static int tg3_test_interrupt(struct tg3 *tp)
if (err)
return err;
- if (int_mbox != 0)
+ if (intr_ok)
return 0;
return -EIO;
@@ -6936,9 +7081,10 @@ static int tg3_open(struct net_device *dev)
if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) {
if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) {
- u32 val = tr32(0x7c04);
+ u32 val = tr32(PCIE_TRANSACTION_CFG);
- tw32(0x7c04, val | (1 << 29));
+ tw32(PCIE_TRANSACTION_CFG,
+ val | PCIE_TRANS_CFG_1SHOT_MSI);
}
}
}
@@ -7857,7 +8003,7 @@ static int tg3_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
if (wol->wolopts & ~WAKE_MAGIC)
return -EINVAL;
if ((wol->wolopts & WAKE_MAGIC) &&
- tp->tg3_flags2 & TG3_FLG2_PHY_SERDES &&
+ tp->tg3_flags2 & TG3_FLG2_ANY_SERDES &&
!(tp->tg3_flags & TG3_FLAG_SERDES_WOL_CAP))
return -EINVAL;
@@ -7893,7 +8039,8 @@ static int tg3_set_tso(struct net_device *dev, u32 value)
return -EINVAL;
return 0;
}
- if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) {
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) {
if (value)
dev->features |= NETIF_F_TSO6;
else
@@ -8147,6 +8294,8 @@ static void tg3_get_ethtool_stats (struct net_device *dev,
#define NVRAM_TEST_SIZE 0x100
#define NVRAM_SELFBOOT_FORMAT1_SIZE 0x14
+#define NVRAM_SELFBOOT_HW_SIZE 0x20
+#define NVRAM_SELFBOOT_DATA_SIZE 0x1c
static int tg3_test_nvram(struct tg3 *tp)
{
@@ -8158,12 +8307,14 @@ static int tg3_test_nvram(struct tg3 *tp)
if (magic == TG3_EEPROM_MAGIC)
size = NVRAM_TEST_SIZE;
- else if ((magic & 0xff000000) == 0xa5000000) {
+ else if ((magic & TG3_EEPROM_MAGIC_FW_MSK) == TG3_EEPROM_MAGIC_FW) {
if ((magic & 0xe00000) == 0x200000)
size = NVRAM_SELFBOOT_FORMAT1_SIZE;
else
return 0;
- } else
+ } else if ((magic & TG3_EEPROM_MAGIC_HW_MSK) == TG3_EEPROM_MAGIC_HW)
+ size = NVRAM_SELFBOOT_HW_SIZE;
+ else
return -EIO;
buf = kmalloc(size, GFP_KERNEL);
@@ -8182,7 +8333,8 @@ static int tg3_test_nvram(struct tg3 *tp)
goto out;
/* Selfboot format */
- if (cpu_to_be32(buf[0]) != TG3_EEPROM_MAGIC) {
+ if ((cpu_to_be32(buf[0]) & TG3_EEPROM_MAGIC_FW_MSK) ==
+ TG3_EEPROM_MAGIC_FW) {
u8 *buf8 = (u8 *) buf, csum8 = 0;
for (i = 0; i < size; i++)
@@ -8197,6 +8349,51 @@ static int tg3_test_nvram(struct tg3 *tp)
goto out;
}
+ if ((cpu_to_be32(buf[0]) & TG3_EEPROM_MAGIC_HW_MSK) ==
+ TG3_EEPROM_MAGIC_HW) {
+ u8 data[NVRAM_SELFBOOT_DATA_SIZE];
+ u8 parity[NVRAM_SELFBOOT_DATA_SIZE];
+ u8 *buf8 = (u8 *) buf;
+ int j, k;
+
+ /* Separate the parity bits and the data bytes. */
+ for (i = 0, j = 0, k = 0; i < NVRAM_SELFBOOT_HW_SIZE; i++) {
+ if ((i == 0) || (i == 8)) {
+ int l;
+ u8 msk;
+
+ for (l = 0, msk = 0x80; l < 7; l++, msk >>= 1)
+ parity[k++] = buf8[i] & msk;
+ i++;
+ }
+ else if (i == 16) {
+ int l;
+ u8 msk;
+
+ for (l = 0, msk = 0x20; l < 6; l++, msk >>= 1)
+ parity[k++] = buf8[i] & msk;
+ i++;
+
+ for (l = 0, msk = 0x80; l < 8; l++, msk >>= 1)
+ parity[k++] = buf8[i] & msk;
+ i++;
+ }
+ data[j++] = buf8[i];
+ }
+
+ err = -EIO;
+ for (i = 0; i < NVRAM_SELFBOOT_DATA_SIZE; i++) {
+ u8 hw8 = hweight8(data[i]);
+
+ if ((hw8 & 0x1) && parity[i])
+ goto out;
+ else if (!(hw8 & 0x1) && !parity[i])
+ goto out;
+ }
+ err = 0;
+ goto out;
+ }
+
/* Bootstrap checksum at offset 0x10 */
csum = calc_crc((unsigned char *) buf, 0x10);
if(csum != cpu_to_le32(buf[0x10/4]))
@@ -8243,7 +8440,7 @@ static int tg3_test_link(struct tg3 *tp)
/* Only test the commonly used registers */
static int tg3_test_registers(struct tg3 *tp)
{
- int i, is_5705;
+ int i, is_5705, is_5750;
u32 offset, read_mask, write_mask, val, save_val, read_val;
static struct {
u16 offset;
@@ -8251,6 +8448,7 @@ static int tg3_test_registers(struct tg3 *tp)
#define TG3_FL_5705 0x1
#define TG3_FL_NOT_5705 0x2
#define TG3_FL_NOT_5788 0x4
+#define TG3_FL_NOT_5750 0x8
u32 read_mask;
u32 write_mask;
} reg_tbl[] = {
@@ -8361,9 +8559,9 @@ static int tg3_test_registers(struct tg3 *tp)
0xffffffff, 0x00000000 },
/* Buffer Manager Control Registers. */
- { BUFMGR_MB_POOL_ADDR, 0x0000,
+ { BUFMGR_MB_POOL_ADDR, TG3_FL_NOT_5750,
0x00000000, 0x007fff80 },
- { BUFMGR_MB_POOL_SIZE, 0x0000,
+ { BUFMGR_MB_POOL_SIZE, TG3_FL_NOT_5750,
0x00000000, 0x007fffff },
{ BUFMGR_MB_RDMA_LOW_WATER, 0x0000,
0x00000000, 0x0000003f },
@@ -8389,10 +8587,12 @@ static int tg3_test_registers(struct tg3 *tp)
{ 0xffff, 0x0000, 0x00000000, 0x00000000 },
};
- if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)
+ is_5705 = is_5750 = 0;
+ if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
is_5705 = 1;
- else
- is_5705 = 0;
+ if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS)
+ is_5750 = 1;
+ }
for (i = 0; reg_tbl[i].offset != 0xffff; i++) {
if (is_5705 && (reg_tbl[i].flags & TG3_FL_NOT_5705))
@@ -8405,6 +8605,9 @@ static int tg3_test_registers(struct tg3 *tp)
(reg_tbl[i].flags & TG3_FL_NOT_5788))
continue;
+ if (is_5750 && (reg_tbl[i].flags & TG3_FL_NOT_5750))
+ continue;
+
offset = (u32) reg_tbl[i].offset;
read_mask = reg_tbl[i].read_mask;
write_mask = reg_tbl[i].write_mask;
@@ -8496,6 +8699,13 @@ static int tg3_test_memory(struct tg3 *tp)
{ 0x00008000, 0x02000},
{ 0x00010000, 0x0c000},
{ 0xffffffff, 0x00000}
+ }, mem_tbl_5906[] = {
+ { 0x00000200, 0x00008},
+ { 0x00004000, 0x00400},
+ { 0x00006000, 0x00400},
+ { 0x00008000, 0x01000},
+ { 0x00010000, 0x01000},
+ { 0xffffffff, 0x00000}
};
struct mem_entry *mem_tbl;
int err = 0;
@@ -8505,6 +8715,8 @@ static int tg3_test_memory(struct tg3 *tp)
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
mem_tbl = mem_tbl_5755;
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ mem_tbl = mem_tbl_5906;
else
mem_tbl = mem_tbl_5705;
} else
@@ -8541,13 +8753,41 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
return 0;
mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) |
- MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY |
- MAC_MODE_PORT_MODE_GMII;
+ MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY;
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ mac_mode |= MAC_MODE_PORT_MODE_MII;
+ else
+ mac_mode |= MAC_MODE_PORT_MODE_GMII;
tw32(MAC_MODE, mac_mode);
} else if (loopback_mode == TG3_PHY_LOOPBACK) {
- tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX |
- BMCR_SPEED1000);
+ u32 val;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 phytest;
+
+ if (!tg3_readphy(tp, MII_TG3_EPHY_TEST, &phytest)) {
+ u32 phy;
+
+ tg3_writephy(tp, MII_TG3_EPHY_TEST,
+ phytest | MII_TG3_EPHY_SHADOW_EN);
+ if (!tg3_readphy(tp, 0x1b, &phy))
+ tg3_writephy(tp, 0x1b, phy & ~0x20);
+ if (!tg3_readphy(tp, 0x10, &phy))
+ tg3_writephy(tp, 0x10, phy & ~0x4000);
+ tg3_writephy(tp, MII_TG3_EPHY_TEST, phytest);
+ }
+ }
+ val = BMCR_LOOPBACK | BMCR_FULLDPLX;
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ val |= BMCR_SPEED100;
+ else
+ val |= BMCR_SPEED1000;
+
+ tg3_writephy(tp, MII_BMCR, val);
udelay(40);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ tg3_writephy(tp, MII_TG3_EPHY_PTEST, 0x1800);
+
/* reset to prevent losing 1st rx packet intermittently */
if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) {
tw32_f(MAC_RX_MODE, RX_MODE_RESET);
@@ -8555,7 +8795,11 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
tw32_f(MAC_RX_MODE, tp->rx_mode);
}
mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) |
- MAC_MODE_LINK_POLARITY | MAC_MODE_PORT_MODE_GMII;
+ MAC_MODE_LINK_POLARITY;
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ mac_mode |= MAC_MODE_PORT_MODE_MII;
+ else
+ mac_mode |= MAC_MODE_PORT_MODE_GMII;
if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) {
mac_mode &= ~MAC_MODE_LINK_POLARITY;
tg3_writephy(tp, MII_TG3_EXT_CTRL,
@@ -8604,7 +8848,8 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
udelay(10);
- for (i = 0; i < 10; i++) {
+ /* 250 usec to allow enough time on some 10/100 Mbps devices. */
+ for (i = 0; i < 25; i++) {
tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE |
HOSTCC_MODE_NOW);
@@ -8956,7 +9201,9 @@ static void __devinit tg3_get_eeprom_size(struct tg3 *tp)
if (tg3_nvram_read_swab(tp, 0, &magic) != 0)
return;
- if ((magic != TG3_EEPROM_MAGIC) && ((magic & 0xff000000) != 0xa5000000))
+ if ((magic != TG3_EEPROM_MAGIC) &&
+ ((magic & TG3_EEPROM_MAGIC_FW_MSK) != TG3_EEPROM_MAGIC_FW) &&
+ ((magic & TG3_EEPROM_MAGIC_HW_MSK) != TG3_EEPROM_MAGIC_HW))
return;
/*
@@ -9194,6 +9441,13 @@ static void __devinit tg3_get_5787_nvram_info(struct tg3 *tp)
}
}
+static void __devinit tg3_get_5906_nvram_info(struct tg3 *tp)
+{
+ tp->nvram_jedecnum = JEDEC_ATMEL;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE;
+}
+
/* Chips other than 5700/5701 use the NVRAM for fetching info. */
static void __devinit tg3_nvram_init(struct tg3 *tp)
{
@@ -9230,6 +9484,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp)
tg3_get_5755_nvram_info(tp);
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
tg3_get_5787_nvram_info(tp);
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ tg3_get_5906_nvram_info(tp);
else
tg3_get_nvram_info(tp);
@@ -9703,6 +9959,12 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
/* Assume an onboard device by default. */
tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ if (!(tr32(PCIE_TRANSACTION_CFG) & PCIE_TRANS_CFG_LOM))
+ tp->tg3_flags &= ~TG3_FLAG_EEPROM_WRITE_PROT;
+ return;
+ }
+
tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val);
if (val == NIC_SRAM_DATA_SIG_MAGIC) {
u32 nic_cfg, led_cfg;
@@ -10034,7 +10296,10 @@ static void __devinit tg3_read_partno(struct tg3 *tp)
}
out_not_found:
- strcpy(tp->board_part_number, "none");
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ strcpy(tp->board_part_number, "BCM95906");
+ else
+ strcpy(tp->board_part_number, "none");
}
static void __devinit tg3_read_fw_ver(struct tg3 *tp)
@@ -10236,6 +10501,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 ||
(tp->tg3_flags2 & TG3_FLG2_5780_CLASS))
tp->tg3_flags2 |= TG3_FLG2_5750_PLUS;
@@ -10245,7 +10511,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) {
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2;
tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI;
} else {
@@ -10262,7 +10529,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5755 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787)
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)
tp->tg3_flags2 |= TG3_FLG2_JUMBO_CAPABLE;
if (pci_find_capability(tp->pdev, PCI_CAP_ID_EXP) != 0)
@@ -10392,6 +10660,12 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pci_cmd &= ~PCI_COMMAND_MEMORY;
pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ tp->read32_mbox = tg3_read32_mbox_5906;
+ tp->write32_mbox = tg3_write32_mbox_5906;
+ tp->write32_tx_mbox = tg3_write32_mbox_5906;
+ tp->write32_rx_mbox = tg3_write32_mbox_5906;
+ }
if (tp->write32 == tg3_write_indirect_reg32 ||
((tp->tg3_flags & TG3_FLAG_PCIX_MODE) &&
@@ -10463,6 +10737,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
(tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) &&
(tp->pci_chip_rev_id != CHIPREV_ID_5705_A1)) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) ||
(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES))
tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED;
@@ -10476,7 +10751,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
tp->tg3_flags2 |= TG3_FLG2_PHY_JITTER_BUG;
- else
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)
tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG;
}
@@ -10566,7 +10841,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->pdev->device == PCI_DEVICE_ID_TIGON3_5705F)) ||
(tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM &&
(tp->pdev->device == PCI_DEVICE_ID_TIGON3_5751F ||
- tp->pdev->device == PCI_DEVICE_ID_TIGON3_5753F)))
+ tp->pdev->device == PCI_DEVICE_ID_TIGON3_5753F)) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tp->tg3_flags |= TG3_FLAG_10_100_ONLY;
err = tg3_phy_probe(tp);
@@ -10617,7 +10893,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
* straddle the 4GB address boundary in some cases.
*/
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tp->dev->hard_start_xmit = tg3_start_xmit;
else
tp->dev->hard_start_xmit = tg3_start_xmit_dma_bug;
@@ -10698,6 +10975,8 @@ static int __devinit tg3_get_device_address(struct tg3 *tp)
else
tg3_nvram_unlock(tp);
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ mac_offset = 0x10;
/* First try to get it from MAC address mailbox. */
tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi);
@@ -11181,6 +11460,12 @@ static void __devinit tg3_init_bufmgr_config(struct tg3 *tp)
DEFAULT_MB_MACRX_LOW_WATER_5705;
tp->bufmgr_config.mbuf_high_water =
DEFAULT_MB_HIGH_WATER_5705;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ tp->bufmgr_config.mbuf_mac_rx_low_water =
+ DEFAULT_MB_MACRX_LOW_WATER_5906;
+ tp->bufmgr_config.mbuf_high_water =
+ DEFAULT_MB_HIGH_WATER_5906;
+ }
tp->bufmgr_config.mbuf_read_dma_low_water_jumbo =
DEFAULT_MB_RDMA_LOW_WATER_JUMBO_5780;
@@ -11224,6 +11509,8 @@ static char * __devinit tg3_phy_string(struct tg3 *tp)
case PHY_ID_BCM5780: return "5780";
case PHY_ID_BCM5755: return "5755";
case PHY_ID_BCM5787: return "5787";
+ case PHY_ID_BCM5756: return "5722/5756";
+ case PHY_ID_BCM5906: return "5906";
case PHY_ID_BCM8002: return "8002/serdes";
case 0: return "serdes";
default: return "unknown";
@@ -11526,7 +11813,8 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
*/
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
dev->features |= NETIF_F_TSO;
- if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2)
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906))
dev->features |= NETIF_F_TSO6;
}
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index 3ecf356cfb0..92f53000bce 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -24,6 +24,8 @@
#define RX_COPY_THRESHOLD 256
+#define TG3_RX_INTERNAL_RING_SZ_5906 32
+
#define RX_STD_MAX_SIZE 1536
#define RX_STD_MAX_SIZE_5705 512
#define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */
@@ -129,6 +131,7 @@
#define CHIPREV_ID_5752_A0_HW 0x5000
#define CHIPREV_ID_5752_A0 0x6000
#define CHIPREV_ID_5752_A1 0x6001
+#define CHIPREV_ID_5906_A1 0xc001
#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12)
#define ASIC_REV_5700 0x07
#define ASIC_REV_5701 0x00
@@ -141,6 +144,7 @@
#define ASIC_REV_5714 0x09
#define ASIC_REV_5755 0x0a
#define ASIC_REV_5787 0x0b
+#define ASIC_REV_5906 0x0c
#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8)
#define CHIPREV_5700_AX 0x70
#define CHIPREV_5700_BX 0x71
@@ -646,7 +650,8 @@
#define SNDDATAI_SCTRL_FORCE_ZERO 0x00000010
#define SNDDATAI_STATSENAB 0x00000c0c
#define SNDDATAI_STATSINCMASK 0x00000c10
-/* 0xc14 --> 0xc80 unused */
+#define ISO_PKT_TX 0x00000c20
+/* 0xc24 --> 0xc80 unused */
#define SNDDATAI_COS_CNT_0 0x00000c80
#define SNDDATAI_COS_CNT_1 0x00000c84
#define SNDDATAI_COS_CNT_2 0x00000c88
@@ -997,11 +1002,13 @@
#define BUFMGR_MB_MACRX_LOW_WATER 0x00004414
#define DEFAULT_MB_MACRX_LOW_WATER 0x00000020
#define DEFAULT_MB_MACRX_LOW_WATER_5705 0x00000010
+#define DEFAULT_MB_MACRX_LOW_WATER_5906 0x00000004
#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO 0x00000098
#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO_5780 0x0000004b
#define BUFMGR_MB_HIGH_WATER 0x00004418
#define DEFAULT_MB_HIGH_WATER 0x00000060
#define DEFAULT_MB_HIGH_WATER_5705 0x00000060
+#define DEFAULT_MB_HIGH_WATER_5906 0x00000010
#define DEFAULT_MB_HIGH_WATER_JUMBO 0x0000017c
#define DEFAULT_MB_HIGH_WATER_JUMBO_5780 0x00000096
#define BUFMGR_RX_MB_ALLOC_REQ 0x0000441c
@@ -1138,7 +1145,12 @@
#define TX_CPU_STATE 0x00005404
#define TX_CPU_PGMCTR 0x0000541c
+#define VCPU_STATUS 0x00005100
+#define VCPU_STATUS_INIT_DONE 0x04000000
+#define VCPU_STATUS_DRV_RESET 0x08000000
+
/* Mailboxes */
+#define GRCMBOX_BASE 0x00005600
#define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */
#define GRCMBOX_INTERRUPT_1 0x00005808 /* 64-bit */
#define GRCMBOX_INTERRUPT_2 0x00005810 /* 64-bit */
@@ -1398,7 +1410,10 @@
#define GRC_EEPROM_CTRL 0x00006840
#define GRC_MDI_CTRL 0x00006844
#define GRC_SEEPROM_DELAY 0x00006848
-/* 0x684c --> 0x6c00 unused */
+/* 0x684c --> 0x6890 unused */
+#define GRC_VCPU_EXT_CTRL 0x00006890
+#define GRC_VCPU_EXT_CTRL_HALT_CPU 0x00400000
+#define GRC_VCPU_EXT_CTRL_DISABLE_WOL 0x20000000
#define GRC_FASTBOOT_PC 0x00006894 /* 5752, 5755, 5787 */
/* 0x6c00 --> 0x7000 unused */
@@ -1485,9 +1500,17 @@
#define NVRAM_WRITE1 0x00007028
/* 0x702c --> 0x7400 unused */
-/* 0x7400 --> 0x8000 unused */
+/* 0x7400 --> 0x7c00 unused */
+#define PCIE_TRANSACTION_CFG 0x00007c04
+#define PCIE_TRANS_CFG_1SHOT_MSI 0x20000000
+#define PCIE_TRANS_CFG_LOM 0x00000020
+
#define TG3_EEPROM_MAGIC 0x669955aa
+#define TG3_EEPROM_MAGIC_FW 0xa5000000
+#define TG3_EEPROM_MAGIC_FW_MSK 0xff000000
+#define TG3_EEPROM_MAGIC_HW 0xabcd
+#define TG3_EEPROM_MAGIC_HW_MSK 0xffff
/* 32K Window into NIC internal memory */
#define NIC_SRAM_WIN_BASE 0x00008000
@@ -1537,6 +1560,7 @@
#define FWCMD_NICDRV_FIX_DMAR 0x00000005
#define FWCMD_NICDRV_FIX_DMAW 0x00000006
#define FWCMD_NICDRV_ALIVE2 0x0000000d
+#define FWCMD_NICDRV_ALIVE3 0x0000000e
#define NIC_SRAM_FW_CMD_LEN_MBOX 0x00000b7c
#define NIC_SRAM_FW_CMD_DATA_MBOX 0x00000b80
#define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00
@@ -1604,6 +1628,7 @@
#define MII_TG3_DSP_RW_PORT 0x15 /* DSP coefficient read/write port */
#define MII_TG3_DSP_ADDRESS 0x17 /* DSP address register */
+#define MII_TG3_EPHY_PTEST 0x17 /* 5906 PHY register */
#define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */
@@ -1617,6 +1642,8 @@
#define MII_TG3_AUX_STAT_100FULL 0x0500
#define MII_TG3_AUX_STAT_1000HALF 0x0600
#define MII_TG3_AUX_STAT_1000FULL 0x0700
+#define MII_TG3_AUX_STAT_100 0x0008
+#define MII_TG3_AUX_STAT_FULL 0x0001
#define MII_TG3_ISTAT 0x1a /* IRQ status register */
#define MII_TG3_IMASK 0x1b /* IRQ mask register */
@@ -1627,6 +1654,9 @@
#define MII_TG3_INT_DUPLEXCHG 0x0008
#define MII_TG3_INT_ANEG_PAGE_RX 0x0400
+#define MII_TG3_EPHY_TEST 0x1f /* 5906 PHY register */
+#define MII_TG3_EPHY_SHADOW_EN 0x80
+
/* There are two ways to manage the TX descriptors on the tigon3.
* Either the descriptors are in host DMA'able memory, or they
* exist only in the cards on-chip SRAM. All 16 send bds are under
@@ -2203,7 +2233,6 @@ struct tg3 {
#define TG3_FLG2_PCI_EXPRESS 0x00000200
#define TG3_FLG2_ASF_NEW_HANDSHAKE 0x00000400
#define TG3_FLG2_HW_AUTONEG 0x00000800
-#define TG3_FLG2_PHY_JUST_INITTED 0x00001000
#define TG3_FLG2_PHY_SERDES 0x00002000
#define TG3_FLG2_CAPACITIVE_COUPLING 0x00004000
#define TG3_FLG2_FLASH 0x00008000
@@ -2236,6 +2265,12 @@ struct tg3 {
u16 asf_counter;
u16 asf_multiplier;
+ /* 1 second counter for transient serdes link events */
+ u32 serdes_counter;
+#define SERDES_AN_TIMEOUT_5704S 2
+#define SERDES_PARALLEL_DET_TIMEOUT 1
+#define SERDES_AN_TIMEOUT_5714S 1
+
struct tg3_link_config link_config;
struct tg3_bufmgr_config bufmgr_config;
@@ -2276,6 +2311,8 @@ struct tg3 {
#define PHY_ID_BCM5780 0x60008350
#define PHY_ID_BCM5755 0xbc050cc0
#define PHY_ID_BCM5787 0xbc050ce0
+#define PHY_ID_BCM5756 0xbc050ed0
+#define PHY_ID_BCM5906 0xdc00ac40
#define PHY_ID_BCM8002 0x60010140
#define PHY_ID_INVALID 0xffffffff
#define PHY_ID_REV_MASK 0x0000000f
@@ -2302,7 +2339,8 @@ struct tg3 {
(X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || \
(X) == PHY_ID_BCM5752 || (X) == PHY_ID_BCM5714 || \
(X) == PHY_ID_BCM5780 || (X) == PHY_ID_BCM5787 || \
- (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM8002)
+ (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM5756 || \
+ (X) == PHY_ID_BCM5906 || (X) == PHY_ID_BCM8002)
struct tg3_hw_stats *hw_stats;
dma_addr_t stats_mapping;
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index 0d66700c6ce..bfc8c3eae9a 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -1876,7 +1876,6 @@ static int sprintf_info(char *buffer, struct net_device *dev)
datap[size+1]=io_word & 0xff;
}
-
size = sprintf(buffer, "\n%6s: Adapter Address : Node Address : Functional Addr\n", dev->name);
size += sprintf(buffer + size,
@@ -1932,64 +1931,6 @@ static int sprintf_info(char *buffer, struct net_device *dev)
#endif
#endif
-#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-static int streamer_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- int i;
- struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
- u8 __iomem *streamer_mmio = streamer_priv->streamer_mmio;
-
- switch(cmd) {
- case IOCTL_SISR_MASK:
- writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
- break;
- case IOCTL_SPIN_LOCK_TEST:
- printk(KERN_INFO "spin_lock() called.\n");
- spin_lock(&streamer_priv->streamer_lock);
- spin_unlock(&streamer_priv->streamer_lock);
- printk(KERN_INFO "spin_unlock() finished.\n");
- break;
- case IOCTL_PRINT_BDAS:
- printk(KERN_INFO "bdas: RXBDA: %x RXLBDA: %x TX2FDA: %x TX2LFDA: %x\n",
- readw(streamer_mmio + RXBDA),
- readw(streamer_mmio + RXLBDA),
- readw(streamer_mmio + TX2FDA),
- readw(streamer_mmio + TX2LFDA));
- break;
- case IOCTL_PRINT_REGISTERS:
- printk(KERN_INFO "registers:\n");
- printk(KERN_INFO "SISR: %04x MISR: %04x LISR: %04x BCTL: %04x BMCTL: %04x\nmask %04x mask %04x\n",
- readw(streamer_mmio + SISR),
- readw(streamer_mmio + MISR_RUM),
- readw(streamer_mmio + LISR),
- readw(streamer_mmio + BCTL),
- readw(streamer_mmio + BMCTL_SUM),
- readw(streamer_mmio + SISR_MASK),
- readw(streamer_mmio + MISR_MASK));
- break;
- case IOCTL_PRINT_RX_BUFS:
- printk(KERN_INFO "Print rx bufs:\n");
- for(i=0; i<STREAMER_RX_RING_SIZE; i++)
- printk(KERN_INFO "rx_ring %d status: 0x%x\n", i,
- streamer_priv->streamer_rx_ring[i].status);
- break;
- case IOCTL_PRINT_TX_BUFS:
- printk(KERN_INFO "Print tx bufs:\n");
- for(i=0; i<STREAMER_TX_RING_SIZE; i++)
- printk(KERN_INFO "tx_ring %d status: 0x%x\n", i,
- streamer_priv->streamer_tx_ring[i].status);
- break;
- case IOCTL_RX_CMD:
- streamer_rx(dev);
- printk(KERN_INFO "Sent rx command.\n");
- break;
- default:
- printk(KERN_INFO "Bad ioctl!\n");
- }
- return 0;
-}
-#endif
-
static struct pci_driver streamer_pci_driver = {
.name = "lanstreamer",
.id_table = streamer_pci_tbl,
diff --git a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h
index 5557d8e1e22..e7bb3494afc 100644
--- a/drivers/net/tokenring/lanstreamer.h
+++ b/drivers/net/tokenring/lanstreamer.h
@@ -62,18 +62,6 @@
#include <linux/version.h>
-#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-#include <asm/ioctl.h>
-#define IOCTL_PRINT_RX_BUFS SIOCDEVPRIVATE
-#define IOCTL_PRINT_TX_BUFS SIOCDEVPRIVATE+1
-#define IOCTL_RX_CMD SIOCDEVPRIVATE+2
-#define IOCTL_TX_CMD SIOCDEVPRIVATE+3
-#define IOCTL_PRINT_REGISTERS SIOCDEVPRIVATE+4
-#define IOCTL_PRINT_BDAS SIOCDEVPRIVATE+5
-#define IOCTL_SPIN_LOCK_TEST SIOCDEVPRIVATE+6
-#define IOCTL_SISR_MASK SIOCDEVPRIVATE+7
-#endif
-
/* MAX_INTR - the maximum number of times we can loop
* inside the interrupt function before returning
* control to the OS (maximum value is 256)
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
index e661d0a9cc6..fb5fa7d6888 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/tulip/de4x5.c
@@ -2114,6 +2114,7 @@ static struct eisa_device_id de4x5_eisa_ids[] = {
{ "DEC4250", 0 }, /* 0 is the board name index... */
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, de4x5_eisa_ids);
static struct eisa_driver de4x5_eisa_driver = {
.id_table = de4x5_eisa_ids,
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index 8f6f6fd8b87..d5c32e9caa9 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -333,11 +333,7 @@ enum state_values {
#define TYPHOON_RESET_TIMEOUT_NOSLEEP ((6 * 1000000) / TYPHOON_UDELAY)
#define TYPHOON_WAIT_TIMEOUT ((1000000 / 2) / TYPHOON_UDELAY)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 28)
-#define typhoon_synchronize_irq(x) synchronize_irq()
-#else
#define typhoon_synchronize_irq(x) synchronize_irq(x)
-#endif
#if defined(NETIF_F_TSO)
#define skb_tso_size(x) (skb_shinfo(x)->gso_size)
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index 54b8e492ef9..58b7efbb075 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -154,7 +154,7 @@ config HDLC
If unsure, say N.
config HDLC_RAW
- bool "Raw HDLC support"
+ tristate "Raw HDLC support"
depends on HDLC
help
Generic HDLC driver supporting raw HDLC over WAN connections.
@@ -162,7 +162,7 @@ config HDLC_RAW
If unsure, say N.
config HDLC_RAW_ETH
- bool "Raw HDLC Ethernet device support"
+ tristate "Raw HDLC Ethernet device support"
depends on HDLC
help
Generic HDLC driver supporting raw HDLC Ethernet device emulation
@@ -173,7 +173,7 @@ config HDLC_RAW_ETH
If unsure, say N.
config HDLC_CISCO
- bool "Cisco HDLC support"
+ tristate "Cisco HDLC support"
depends on HDLC
help
Generic HDLC driver supporting Cisco HDLC over WAN connections.
@@ -181,7 +181,7 @@ config HDLC_CISCO
If unsure, say N.
config HDLC_FR
- bool "Frame Relay support"
+ tristate "Frame Relay support"
depends on HDLC
help
Generic HDLC driver supporting Frame Relay over WAN connections.
@@ -189,7 +189,7 @@ config HDLC_FR
If unsure, say N.
config HDLC_PPP
- bool "Synchronous Point-to-Point Protocol (PPP) support"
+ tristate "Synchronous Point-to-Point Protocol (PPP) support"
depends on HDLC
help
Generic HDLC driver supporting PPP over WAN connections.
@@ -197,7 +197,7 @@ config HDLC_PPP
If unsure, say N.
config HDLC_X25
- bool "X.25 protocol support"
+ tristate "X.25 protocol support"
depends on HDLC && (LAPB=m && HDLC=m || LAPB=y)
help
Generic HDLC driver supporting X.25 over WAN connections.
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 316ca6869d5..83ec2c87ba3 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -9,14 +9,13 @@ cyclomx-y := cycx_main.o
cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o
cyclomx-objs := $(cyclomx-y)
-hdlc-y := hdlc_generic.o
-hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o
-hdlc-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
-hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
-hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o
-hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o
-hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o
-hdlc-objs := $(hdlc-y)
+obj-$(CONFIG_HDLC) += hdlc.o
+obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o
+obj-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
+obj-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
+obj-$(CONFIG_HDLC_FR) += hdlc_fr.o
+obj-$(CONFIG_HDLC_PPP) += hdlc_ppp.o syncppp.o
+obj-$(CONFIG_HDLC_X25) += hdlc_x25.o
pc300-y := pc300_drv.o
pc300-$(CONFIG_PC300_MLPPP) += pc300_tty.o
@@ -38,10 +37,6 @@ obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o
obj-$(CONFIG_LAPBETHER) += lapbether.o
obj-$(CONFIG_SBNI) += sbni.o
obj-$(CONFIG_PC300) += pc300.o
-obj-$(CONFIG_HDLC) += hdlc.o
-ifeq ($(CONFIG_HDLC_PPP),y)
- obj-$(CONFIG_HDLC) += syncppp.o
-endif
obj-$(CONFIG_N2) += n2.o
obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o
diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc.c
index 04ca1f7b642..db354e0edbe 100644
--- a/drivers/net/wan/hdlc_generic.c
+++ b/drivers/net/wan/hdlc.c
@@ -1,7 +1,7 @@
/*
* Generic HDLC support routines for Linux
*
- * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -17,9 +17,9 @@
* Use sethdlc utility to set line parameters, protocol and PVCs
*
* How does it work:
- * - proto.open(), close(), start(), stop() calls are serialized.
+ * - proto->open(), close(), start(), stop() calls are serialized.
* The order is: open, [ start, stop ... ] close ...
- * - proto.start() and stop() are called with spin_lock_irq held.
+ * - proto->start() and stop() are called with spin_lock_irq held.
*/
#include <linux/module.h>
@@ -38,10 +38,12 @@
#include <linux/hdlc.h>
-static const char* version = "HDLC support module revision 1.19";
+static const char* version = "HDLC support module revision 1.20";
#undef DEBUG_LINK
+static struct hdlc_proto *first_proto = NULL;
+
static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
{
@@ -63,11 +65,11 @@ static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *p, struct net_device *orig_dev)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->proto.netif_rx)
- return hdlc->proto.netif_rx(skb);
+ struct hdlc_device_desc *desc = dev_to_desc(dev);
+ if (desc->netif_rx)
+ return desc->netif_rx(skb);
- hdlc->stats.rx_dropped++; /* Shouldn't happen */
+ desc->stats.rx_dropped++; /* Shouldn't happen */
dev_kfree_skb(skb);
return NET_RX_DROP;
}
@@ -77,8 +79,8 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
static inline void hdlc_proto_start(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->proto.start)
- return hdlc->proto.start(dev);
+ if (hdlc->proto->start)
+ return hdlc->proto->start(dev);
}
@@ -86,8 +88,8 @@ static inline void hdlc_proto_start(struct net_device *dev)
static inline void hdlc_proto_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->proto.stop)
- return hdlc->proto.stop(dev);
+ if (hdlc->proto->stop)
+ return hdlc->proto->stop(dev);
}
@@ -144,15 +146,15 @@ int hdlc_open(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
- printk(KERN_DEBUG "hdlc_open() carrier %i open %i\n",
+ printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name,
hdlc->carrier, hdlc->open);
#endif
- if (hdlc->proto.id == -1)
+ if (hdlc->proto == NULL)
return -ENOSYS; /* no protocol attached */
- if (hdlc->proto.open) {
- int result = hdlc->proto.open(dev);
+ if (hdlc->proto->open) {
+ int result = hdlc->proto->open(dev);
if (result)
return result;
}
@@ -178,7 +180,7 @@ void hdlc_close(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
- printk(KERN_DEBUG "hdlc_close() carrier %i open %i\n",
+ printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name,
hdlc->carrier, hdlc->open);
#endif
@@ -190,68 +192,34 @@ void hdlc_close(struct net_device *dev)
spin_unlock_irq(&hdlc->state_lock);
- if (hdlc->proto.close)
- hdlc->proto.close(dev);
+ if (hdlc->proto->close)
+ hdlc->proto->close(dev);
}
-#ifndef CONFIG_HDLC_RAW
-#define hdlc_raw_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_RAW_ETH
-#define hdlc_raw_eth_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_PPP
-#define hdlc_ppp_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_CISCO
-#define hdlc_cisco_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_FR
-#define hdlc_fr_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_X25
-#define hdlc_x25_ioctl(dev, ifr) -ENOSYS
-#endif
-
-
int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
- unsigned int proto;
+ struct hdlc_proto *proto = first_proto;
+ int result;
if (cmd != SIOCWANDEV)
return -EINVAL;
- switch(ifr->ifr_settings.type) {
- case IF_PROTO_HDLC:
- case IF_PROTO_HDLC_ETH:
- case IF_PROTO_PPP:
- case IF_PROTO_CISCO:
- case IF_PROTO_FR:
- case IF_PROTO_X25:
- proto = ifr->ifr_settings.type;
- break;
-
- default:
- proto = hdlc->proto.id;
+ if (dev_to_hdlc(dev)->proto) {
+ result = dev_to_hdlc(dev)->proto->ioctl(dev, ifr);
+ if (result != -EINVAL)
+ return result;
}
- switch(proto) {
- case IF_PROTO_HDLC: return hdlc_raw_ioctl(dev, ifr);
- case IF_PROTO_HDLC_ETH: return hdlc_raw_eth_ioctl(dev, ifr);
- case IF_PROTO_PPP: return hdlc_ppp_ioctl(dev, ifr);
- case IF_PROTO_CISCO: return hdlc_cisco_ioctl(dev, ifr);
- case IF_PROTO_FR: return hdlc_fr_ioctl(dev, ifr);
- case IF_PROTO_X25: return hdlc_x25_ioctl(dev, ifr);
- default: return -EINVAL;
+ /* Not handled by currently attached protocol (if any) */
+
+ while (proto) {
+ if ((result = proto->ioctl(dev, ifr)) != -EINVAL)
+ return result;
+ proto = proto->next;
}
+ return -EINVAL;
}
void hdlc_setup(struct net_device *dev)
@@ -267,8 +235,6 @@ void hdlc_setup(struct net_device *dev)
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
- hdlc->proto.id = -1;
- hdlc->proto.detach = NULL;
hdlc->carrier = 1;
hdlc->open = 0;
spin_lock_init(&hdlc->state_lock);
@@ -277,7 +243,8 @@ void hdlc_setup(struct net_device *dev)
struct net_device *alloc_hdlcdev(void *priv)
{
struct net_device *dev;
- dev = alloc_netdev(sizeof(hdlc_device), "hdlc%d", hdlc_setup);
+ dev = alloc_netdev(sizeof(struct hdlc_device_desc) +
+ sizeof(hdlc_device), "hdlc%d", hdlc_setup);
if (dev)
dev_to_hdlc(dev)->priv = priv;
return dev;
@@ -286,13 +253,71 @@ struct net_device *alloc_hdlcdev(void *priv)
void unregister_hdlc_device(struct net_device *dev)
{
rtnl_lock();
- hdlc_proto_detach(dev_to_hdlc(dev));
unregister_netdevice(dev);
+ detach_hdlc_protocol(dev);
rtnl_unlock();
}
+int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
+ int (*rx)(struct sk_buff *skb), size_t size)
+{
+ detach_hdlc_protocol(dev);
+
+ if (!try_module_get(proto->module))
+ return -ENOSYS;
+
+ if (size)
+ if ((dev_to_hdlc(dev)->state = kmalloc(size,
+ GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING "Memory squeeze on"
+ " hdlc_proto_attach()\n");
+ module_put(proto->module);
+ return -ENOBUFS;
+ }
+ dev_to_hdlc(dev)->proto = proto;
+ dev_to_desc(dev)->netif_rx = rx;
+ return 0;
+}
+
+
+void detach_hdlc_protocol(struct net_device *dev)
+{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+
+ if (hdlc->proto) {
+ if (hdlc->proto->detach)
+ hdlc->proto->detach(dev);
+ module_put(hdlc->proto->module);
+ hdlc->proto = NULL;
+ }
+ kfree(hdlc->state);
+ hdlc->state = NULL;
+}
+
+
+void register_hdlc_protocol(struct hdlc_proto *proto)
+{
+ proto->next = first_proto;
+ first_proto = proto;
+}
+
+
+void unregister_hdlc_protocol(struct hdlc_proto *proto)
+{
+ struct hdlc_proto **p = &first_proto;
+ while (*p) {
+ if (*p == proto) {
+ *p = proto->next;
+ return;
+ }
+ p = &((*p)->next);
+ }
+}
+
+
+
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("HDLC support module");
MODULE_LICENSE("GPL v2");
@@ -303,6 +328,10 @@ EXPORT_SYMBOL(hdlc_ioctl);
EXPORT_SYMBOL(hdlc_setup);
EXPORT_SYMBOL(alloc_hdlcdev);
EXPORT_SYMBOL(unregister_hdlc_device);
+EXPORT_SYMBOL(register_hdlc_protocol);
+EXPORT_SYMBOL(unregister_hdlc_protocol);
+EXPORT_SYMBOL(attach_hdlc_protocol);
+EXPORT_SYMBOL(detach_hdlc_protocol);
static struct packet_type hdlc_packet_type = {
.type = __constant_htons(ETH_P_HDLC),
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index f289daba0c7..b0bc5ddcf1b 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Cisco HDLC support
*
- * Copyright (C) 2000 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -34,17 +34,56 @@
#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
+struct hdlc_header {
+ u8 address;
+ u8 control;
+ u16 protocol;
+}__attribute__ ((packed));
+
+
+struct cisco_packet {
+ u32 type; /* code */
+ u32 par1;
+ u32 par2;
+ u16 rel; /* reliability */
+ u32 time;
+}__attribute__ ((packed));
+#define CISCO_PACKET_LEN 18
+#define CISCO_BIG_PACKET_LEN 20
+
+
+struct cisco_state {
+ cisco_proto settings;
+
+ struct timer_list timer;
+ unsigned long last_poll;
+ int up;
+ int request_sent;
+ u32 txseq; /* TX sequence number */
+ u32 rxseq; /* RX sequence number */
+};
+
+
+static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr);
+
+
+static inline struct cisco_state * state(hdlc_device *hdlc)
+{
+ return(struct cisco_state *)(hdlc->state);
+}
+
+
static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
u16 type, void *daddr, void *saddr,
unsigned int len)
{
- hdlc_header *data;
+ struct hdlc_header *data;
#ifdef DEBUG_HARD_HEADER
printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
#endif
- skb_push(skb, sizeof(hdlc_header));
- data = (hdlc_header*)skb->data;
+ skb_push(skb, sizeof(struct hdlc_header));
+ data = (struct hdlc_header*)skb->data;
if (type == CISCO_KEEPALIVE)
data->address = CISCO_MULTICAST;
else
@@ -52,7 +91,7 @@ static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
data->control = 0;
data->protocol = htons(type);
- return sizeof(hdlc_header);
+ return sizeof(struct hdlc_header);
}
@@ -61,9 +100,10 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
u32 par1, u32 par2)
{
struct sk_buff *skb;
- cisco_packet *data;
+ struct cisco_packet *data;
- skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet));
+ skb = dev_alloc_skb(sizeof(struct hdlc_header) +
+ sizeof(struct cisco_packet));
if (!skb) {
printk(KERN_WARNING
"%s: Memory squeeze on cisco_keepalive_send()\n",
@@ -72,7 +112,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
}
skb_reserve(skb, 4);
cisco_hard_header(skb, dev, CISCO_KEEPALIVE, NULL, NULL, 0);
- data = (cisco_packet*)(skb->data + 4);
+ data = (struct cisco_packet*)(skb->data + 4);
data->type = htonl(type);
data->par1 = htonl(par1);
@@ -81,7 +121,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
/* we will need do_div here if 1000 % HZ != 0 */
data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ));
- skb_put(skb, sizeof(cisco_packet));
+ skb_put(skb, sizeof(struct cisco_packet));
skb->priority = TC_PRIO_CONTROL;
skb->dev = dev;
skb->nh.raw = skb->data;
@@ -93,9 +133,9 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
static __be16 cisco_type_trans(struct sk_buff *skb, struct net_device *dev)
{
- hdlc_header *data = (hdlc_header*)skb->data;
+ struct hdlc_header *data = (struct hdlc_header*)skb->data;
- if (skb->len < sizeof(hdlc_header))
+ if (skb->len < sizeof(struct hdlc_header))
return __constant_htons(ETH_P_HDLC);
if (data->address != CISCO_MULTICAST &&
@@ -106,7 +146,7 @@ static __be16 cisco_type_trans(struct sk_buff *skb, struct net_device *dev)
case __constant_htons(ETH_P_IP):
case __constant_htons(ETH_P_IPX):
case __constant_htons(ETH_P_IPV6):
- skb_pull(skb, sizeof(hdlc_header));
+ skb_pull(skb, sizeof(struct hdlc_header));
return data->protocol;
default:
return __constant_htons(ETH_P_HDLC);
@@ -118,12 +158,12 @@ static int cisco_rx(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
hdlc_device *hdlc = dev_to_hdlc(dev);
- hdlc_header *data = (hdlc_header*)skb->data;
- cisco_packet *cisco_data;
+ struct hdlc_header *data = (struct hdlc_header*)skb->data;
+ struct cisco_packet *cisco_data;
struct in_device *in_dev;
- u32 addr, mask;
+ __be32 addr, mask;
- if (skb->len < sizeof(hdlc_header))
+ if (skb->len < sizeof(struct hdlc_header))
goto rx_error;
if (data->address != CISCO_MULTICAST &&
@@ -137,15 +177,17 @@ static int cisco_rx(struct sk_buff *skb)
return NET_RX_SUCCESS;
case CISCO_KEEPALIVE:
- if (skb->len != sizeof(hdlc_header) + CISCO_PACKET_LEN &&
- skb->len != sizeof(hdlc_header) + CISCO_BIG_PACKET_LEN) {
- printk(KERN_INFO "%s: Invalid length of Cisco "
- "control packet (%d bytes)\n",
- dev->name, skb->len);
+ if ((skb->len != sizeof(struct hdlc_header) +
+ CISCO_PACKET_LEN) &&
+ (skb->len != sizeof(struct hdlc_header) +
+ CISCO_BIG_PACKET_LEN)) {
+ printk(KERN_INFO "%s: Invalid length of Cisco control"
+ " packet (%d bytes)\n", dev->name, skb->len);
goto rx_error;
}
- cisco_data = (cisco_packet*)(skb->data + sizeof(hdlc_header));
+ cisco_data = (struct cisco_packet*)(skb->data + sizeof
+ (struct hdlc_header));
switch(ntohl (cisco_data->type)) {
case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
@@ -178,11 +220,11 @@ static int cisco_rx(struct sk_buff *skb)
goto rx_error;
case CISCO_KEEPALIVE_REQ:
- hdlc->state.cisco.rxseq = ntohl(cisco_data->par1);
- if (hdlc->state.cisco.request_sent &&
- ntohl(cisco_data->par2)==hdlc->state.cisco.txseq) {
- hdlc->state.cisco.last_poll = jiffies;
- if (!hdlc->state.cisco.up) {
+ state(hdlc)->rxseq = ntohl(cisco_data->par1);
+ if (state(hdlc)->request_sent &&
+ ntohl(cisco_data->par2) == state(hdlc)->txseq) {
+ state(hdlc)->last_poll = jiffies;
+ if (!state(hdlc)->up) {
u32 sec, min, hrs, days;
sec = ntohl(cisco_data->time) / 1000;
min = sec / 60; sec -= min * 60;
@@ -193,7 +235,7 @@ static int cisco_rx(struct sk_buff *skb)
dev->name, days, hrs,
min, sec);
netif_dormant_off(dev);
- hdlc->state.cisco.up = 1;
+ state(hdlc)->up = 1;
}
}
@@ -208,7 +250,7 @@ static int cisco_rx(struct sk_buff *skb)
return NET_RX_DROP;
rx_error:
- hdlc->stats.rx_errors++; /* Mark error */
+ dev_to_desc(dev)->stats.rx_errors++; /* Mark error */
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -220,23 +262,22 @@ static void cisco_timer(unsigned long arg)
struct net_device *dev = (struct net_device *)arg;
hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->state.cisco.up &&
- time_after(jiffies, hdlc->state.cisco.last_poll +
- hdlc->state.cisco.settings.timeout * HZ)) {
- hdlc->state.cisco.up = 0;
+ if (state(hdlc)->up &&
+ time_after(jiffies, state(hdlc)->last_poll +
+ state(hdlc)->settings.timeout * HZ)) {
+ state(hdlc)->up = 0;
printk(KERN_INFO "%s: Link down\n", dev->name);
netif_dormant_on(dev);
}
- cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ,
- ++hdlc->state.cisco.txseq,
- hdlc->state.cisco.rxseq);
- hdlc->state.cisco.request_sent = 1;
- hdlc->state.cisco.timer.expires = jiffies +
- hdlc->state.cisco.settings.interval * HZ;
- hdlc->state.cisco.timer.function = cisco_timer;
- hdlc->state.cisco.timer.data = arg;
- add_timer(&hdlc->state.cisco.timer);
+ cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, ++state(hdlc)->txseq,
+ state(hdlc)->rxseq);
+ state(hdlc)->request_sent = 1;
+ state(hdlc)->timer.expires = jiffies +
+ state(hdlc)->settings.interval * HZ;
+ state(hdlc)->timer.function = cisco_timer;
+ state(hdlc)->timer.data = arg;
+ add_timer(&state(hdlc)->timer);
}
@@ -244,15 +285,15 @@ static void cisco_timer(unsigned long arg)
static void cisco_start(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- hdlc->state.cisco.up = 0;
- hdlc->state.cisco.request_sent = 0;
- hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0;
-
- init_timer(&hdlc->state.cisco.timer);
- hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/
- hdlc->state.cisco.timer.function = cisco_timer;
- hdlc->state.cisco.timer.data = (unsigned long)dev;
- add_timer(&hdlc->state.cisco.timer);
+ state(hdlc)->up = 0;
+ state(hdlc)->request_sent = 0;
+ state(hdlc)->txseq = state(hdlc)->rxseq = 0;
+
+ init_timer(&state(hdlc)->timer);
+ state(hdlc)->timer.expires = jiffies + HZ; /*First poll after 1s*/
+ state(hdlc)->timer.function = cisco_timer;
+ state(hdlc)->timer.data = (unsigned long)dev;
+ add_timer(&state(hdlc)->timer);
}
@@ -260,15 +301,24 @@ static void cisco_start(struct net_device *dev)
static void cisco_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- del_timer_sync(&hdlc->state.cisco.timer);
+ del_timer_sync(&state(hdlc)->timer);
netif_dormant_on(dev);
- hdlc->state.cisco.up = 0;
- hdlc->state.cisco.request_sent = 0;
+ state(hdlc)->up = 0;
+ state(hdlc)->request_sent = 0;
}
-int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .start = cisco_start,
+ .stop = cisco_stop,
+ .type_trans = cisco_type_trans,
+ .ioctl = cisco_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
{
cisco_proto __user *cisco_s = ifr->ifr_settings.ifs_ifsu.cisco;
const size_t size = sizeof(cisco_proto);
@@ -278,12 +328,14 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_CISCO;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size))
+ if (copy_to_user(cisco_s, &state(hdlc)->settings, size))
return -EFAULT;
return 0;
@@ -302,19 +354,15 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
-
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memcpy(&hdlc->state.cisco.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
+ result = attach_hdlc_protocol(dev, &proto, cisco_rx,
+ sizeof(struct cisco_state));
+ if (result)
+ return result;
- hdlc->proto.start = cisco_start;
- hdlc->proto.stop = cisco_stop;
- hdlc->proto.netif_rx = cisco_rx;
- hdlc->proto.type_trans = cisco_type_trans;
- hdlc->proto.id = IF_PROTO_CISCO;
+ memcpy(&state(hdlc)->settings, &new_settings, size);
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = cisco_hard_header;
dev->hard_header_cache = NULL;
@@ -327,3 +375,25 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Cisco HDLC protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index 7bb737bbdeb..b45ab680d2d 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Frame Relay support
*
- * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -52,6 +52,8 @@
#undef DEBUG_PKT
#undef DEBUG_ECN
#undef DEBUG_LINK
+#undef DEBUG_PROTO
+#undef DEBUG_PVC
#define FR_UI 0x03
#define FR_PAD 0x00
@@ -115,13 +117,53 @@ typedef struct {
}__attribute__ ((packed)) fr_hdr;
+typedef struct pvc_device_struct {
+ struct net_device *frad;
+ struct net_device *main;
+ struct net_device *ether; /* bridged Ethernet interface */
+ struct pvc_device_struct *next; /* Sorted in ascending DLCI order */
+ int dlci;
+ int open_count;
+
+ struct {
+ unsigned int new: 1;
+ unsigned int active: 1;
+ unsigned int exist: 1;
+ unsigned int deleted: 1;
+ unsigned int fecn: 1;
+ unsigned int becn: 1;
+ unsigned int bandwidth; /* Cisco LMI reporting only */
+ }state;
+}pvc_device;
+
+
+struct frad_state {
+ fr_proto settings;
+ pvc_device *first_pvc;
+ int dce_pvc_count;
+
+ struct timer_list timer;
+ unsigned long last_poll;
+ int reliable;
+ int dce_changed;
+ int request;
+ int fullrep_sent;
+ u32 last_errors; /* last errors bit list */
+ u8 n391cnt;
+ u8 txseq; /* TX sequence number */
+ u8 rxseq; /* RX sequence number */
+};
+
+
+static int fr_ioctl(struct net_device *dev, struct ifreq *ifr);
+
+
static inline u16 q922_to_dlci(u8 *hdr)
{
return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
}
-
static inline void dlci_to_q922(u8 *hdr, u16 dlci)
{
hdr[0] = (dlci >> 2) & 0xFC;
@@ -129,10 +171,21 @@ static inline void dlci_to_q922(u8 *hdr, u16 dlci)
}
+static inline struct frad_state * state(hdlc_device *hdlc)
+{
+ return(struct frad_state *)(hdlc->state);
+}
+
+
+static __inline__ pvc_device* dev_to_pvc(struct net_device *dev)
+{
+ return dev->priv;
+}
+
static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
{
- pvc_device *pvc = hdlc->state.fr.first_pvc;
+ pvc_device *pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->dlci == dlci)
@@ -146,10 +199,10 @@ static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
}
-static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
+static pvc_device* add_pvc(struct net_device *dev, u16 dlci)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc;
+ pvc_device *pvc, **pvc_p = &state(hdlc)->first_pvc;
while (*pvc_p) {
if ((*pvc_p)->dlci == dlci)
@@ -160,12 +213,15 @@ static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
}
pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC);
+#ifdef DEBUG_PVC
+ printk(KERN_DEBUG "add_pvc: allocated pvc %p, frad %p\n", pvc, dev);
+#endif
if (!pvc)
return NULL;
memset(pvc, 0, sizeof(pvc_device));
pvc->dlci = dlci;
- pvc->master = dev;
+ pvc->frad = dev;
pvc->next = *pvc_p; /* Put it in the chain */
*pvc_p = pvc;
return pvc;
@@ -174,7 +230,7 @@ static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
static inline int pvc_is_used(pvc_device *pvc)
{
- return pvc->main != NULL || pvc->ether != NULL;
+ return pvc->main || pvc->ether;
}
@@ -200,11 +256,14 @@ static inline void pvc_carrier(int on, pvc_device *pvc)
static inline void delete_unused_pvcs(hdlc_device *hdlc)
{
- pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+ pvc_device **pvc_p = &state(hdlc)->first_pvc;
while (*pvc_p) {
if (!pvc_is_used(*pvc_p)) {
pvc_device *pvc = *pvc_p;
+#ifdef DEBUG_PVC
+ printk(KERN_DEBUG "freeing unused pvc: %p\n", pvc);
+#endif
*pvc_p = pvc->next;
kfree(pvc);
continue;
@@ -295,16 +354,16 @@ static int pvc_open(struct net_device *dev)
{
pvc_device *pvc = dev_to_pvc(dev);
- if ((pvc->master->flags & IFF_UP) == 0)
- return -EIO; /* Master must be UP in order to activate PVC */
+ if ((pvc->frad->flags & IFF_UP) == 0)
+ return -EIO; /* Frad must be UP in order to activate PVC */
if (pvc->open_count++ == 0) {
- hdlc_device *hdlc = dev_to_hdlc(pvc->master);
- if (hdlc->state.fr.settings.lmi == LMI_NONE)
- pvc->state.active = netif_carrier_ok(pvc->master);
+ hdlc_device *hdlc = dev_to_hdlc(pvc->frad);
+ if (state(hdlc)->settings.lmi == LMI_NONE)
+ pvc->state.active = netif_carrier_ok(pvc->frad);
pvc_carrier(pvc->state.active, pvc);
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->dce_changed = 1;
}
return 0;
}
@@ -316,12 +375,12 @@ static int pvc_close(struct net_device *dev)
pvc_device *pvc = dev_to_pvc(dev);
if (--pvc->open_count == 0) {
- hdlc_device *hdlc = dev_to_hdlc(pvc->master);
- if (hdlc->state.fr.settings.lmi == LMI_NONE)
+ hdlc_device *hdlc = dev_to_hdlc(pvc->frad);
+ if (state(hdlc)->settings.lmi == LMI_NONE)
pvc->state.active = 0;
- if (hdlc->state.fr.settings.dce) {
- hdlc->state.fr.dce_changed = 1;
+ if (state(hdlc)->settings.dce) {
+ state(hdlc)->dce_changed = 1;
pvc->state.active = 0;
}
}
@@ -348,7 +407,7 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
info.dlci = pvc->dlci;
- memcpy(info.master, pvc->master->name, IFNAMSIZ);
+ memcpy(info.master, pvc->frad->name, IFNAMSIZ);
if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info,
&info, sizeof(info)))
return -EFAULT;
@@ -361,7 +420,7 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static inline struct net_device_stats *pvc_get_stats(struct net_device *dev)
{
- return netdev_priv(dev);
+ return &dev_to_desc(dev)->stats;
}
@@ -393,7 +452,7 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_packets++;
if (pvc->state.fecn) /* TX Congestion counter */
stats->tx_compressed++;
- skb->dev = pvc->master;
+ skb->dev = pvc->frad;
dev_queue_xmit(skb);
return 0;
}
@@ -419,7 +478,7 @@ static int pvc_change_mtu(struct net_device *dev, int new_mtu)
static inline void fr_log_dlci_active(pvc_device *pvc)
{
printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n",
- pvc->master->name,
+ pvc->frad->name,
pvc->dlci,
pvc->main ? pvc->main->name : "",
pvc->main && pvc->ether ? " " : "",
@@ -438,21 +497,20 @@ static inline u8 fr_lmi_nextseq(u8 x)
}
-
static void fr_lmi_send(struct net_device *dev, int fullrep)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
struct sk_buff *skb;
- pvc_device *pvc = hdlc->state.fr.first_pvc;
- int lmi = hdlc->state.fr.settings.lmi;
- int dce = hdlc->state.fr.settings.dce;
+ pvc_device *pvc = state(hdlc)->first_pvc;
+ int lmi = state(hdlc)->settings.lmi;
+ int dce = state(hdlc)->settings.dce;
int len = lmi == LMI_ANSI ? LMI_ANSI_LENGTH : LMI_CCITT_CISCO_LENGTH;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3;
u8 *data;
int i = 0;
if (dce && fullrep) {
- len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
+ len += state(hdlc)->dce_pvc_count * (2 + stat_len);
if (len > HDLC_MAX_MRU) {
printk(KERN_WARNING "%s: Too many PVCs while sending "
"LMI full report\n", dev->name);
@@ -486,8 +544,9 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
data[i++] = lmi == LMI_CCITT ? LMI_CCITT_ALIVE : LMI_ANSI_CISCO_ALIVE;
data[i++] = LMI_INTEG_LEN;
- data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
- data[i++] = hdlc->state.fr.rxseq;
+ data[i++] = state(hdlc)->txseq =
+ fr_lmi_nextseq(state(hdlc)->txseq);
+ data[i++] = state(hdlc)->rxseq;
if (dce && fullrep) {
while (pvc) {
@@ -496,7 +555,7 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
data[i++] = stat_len;
/* LMI start/restart */
- if (hdlc->state.fr.reliable && !pvc->state.exist) {
+ if (state(hdlc)->reliable && !pvc->state.exist) {
pvc->state.exist = pvc->state.new = 1;
fr_log_dlci_active(pvc);
}
@@ -541,15 +600,15 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
static void fr_set_link_state(int reliable, struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- pvc_device *pvc = hdlc->state.fr.first_pvc;
+ pvc_device *pvc = state(hdlc)->first_pvc;
- hdlc->state.fr.reliable = reliable;
+ state(hdlc)->reliable = reliable;
if (reliable) {
netif_dormant_off(dev);
- hdlc->state.fr.n391cnt = 0; /* Request full status */
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->n391cnt = 0; /* Request full status */
+ state(hdlc)->dce_changed = 1;
- if (hdlc->state.fr.settings.lmi == LMI_NONE) {
+ if (state(hdlc)->settings.lmi == LMI_NONE) {
while (pvc) { /* Activate all PVCs */
pvc_carrier(1, pvc);
pvc->state.exist = pvc->state.active = 1;
@@ -563,7 +622,7 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
pvc_carrier(0, pvc);
pvc->state.exist = pvc->state.active = 0;
pvc->state.new = 0;
- if (!hdlc->state.fr.settings.dce)
+ if (!state(hdlc)->settings.dce)
pvc->state.bandwidth = 0;
pvc = pvc->next;
}
@@ -571,7 +630,6 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
}
-
static void fr_timer(unsigned long arg)
{
struct net_device *dev = (struct net_device *)arg;
@@ -579,62 +637,61 @@ static void fr_timer(unsigned long arg)
int i, cnt = 0, reliable;
u32 list;
- if (hdlc->state.fr.settings.dce) {
- reliable = hdlc->state.fr.request &&
- time_before(jiffies, hdlc->state.fr.last_poll +
- hdlc->state.fr.settings.t392 * HZ);
- hdlc->state.fr.request = 0;
+ if (state(hdlc)->settings.dce) {
+ reliable = state(hdlc)->request &&
+ time_before(jiffies, state(hdlc)->last_poll +
+ state(hdlc)->settings.t392 * HZ);
+ state(hdlc)->request = 0;
} else {
- hdlc->state.fr.last_errors <<= 1; /* Shift the list */
- if (hdlc->state.fr.request) {
- if (hdlc->state.fr.reliable)
+ state(hdlc)->last_errors <<= 1; /* Shift the list */
+ if (state(hdlc)->request) {
+ if (state(hdlc)->reliable)
printk(KERN_INFO "%s: No LMI status reply "
"received\n", dev->name);
- hdlc->state.fr.last_errors |= 1;
+ state(hdlc)->last_errors |= 1;
}
- list = hdlc->state.fr.last_errors;
- for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1)
+ list = state(hdlc)->last_errors;
+ for (i = 0; i < state(hdlc)->settings.n393; i++, list >>= 1)
cnt += (list & 1); /* errors count */
- reliable = (cnt < hdlc->state.fr.settings.n392);
+ reliable = (cnt < state(hdlc)->settings.n392);
}
- if (hdlc->state.fr.reliable != reliable) {
+ if (state(hdlc)->reliable != reliable) {
printk(KERN_INFO "%s: Link %sreliable\n", dev->name,
reliable ? "" : "un");
fr_set_link_state(reliable, dev);
}
- if (hdlc->state.fr.settings.dce)
- hdlc->state.fr.timer.expires = jiffies +
- hdlc->state.fr.settings.t392 * HZ;
+ if (state(hdlc)->settings.dce)
+ state(hdlc)->timer.expires = jiffies +
+ state(hdlc)->settings.t392 * HZ;
else {
- if (hdlc->state.fr.n391cnt)
- hdlc->state.fr.n391cnt--;
+ if (state(hdlc)->n391cnt)
+ state(hdlc)->n391cnt--;
- fr_lmi_send(dev, hdlc->state.fr.n391cnt == 0);
+ fr_lmi_send(dev, state(hdlc)->n391cnt == 0);
- hdlc->state.fr.last_poll = jiffies;
- hdlc->state.fr.request = 1;
- hdlc->state.fr.timer.expires = jiffies +
- hdlc->state.fr.settings.t391 * HZ;
+ state(hdlc)->last_poll = jiffies;
+ state(hdlc)->request = 1;
+ state(hdlc)->timer.expires = jiffies +
+ state(hdlc)->settings.t391 * HZ;
}
- hdlc->state.fr.timer.function = fr_timer;
- hdlc->state.fr.timer.data = arg;
- add_timer(&hdlc->state.fr.timer);
+ state(hdlc)->timer.function = fr_timer;
+ state(hdlc)->timer.data = arg;
+ add_timer(&state(hdlc)->timer);
}
-
static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
pvc_device *pvc;
u8 rxseq, txseq;
- int lmi = hdlc->state.fr.settings.lmi;
- int dce = hdlc->state.fr.settings.dce;
+ int lmi = state(hdlc)->settings.lmi;
+ int dce = state(hdlc)->settings.dce;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3, reptype, error, no_ram, i;
if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH :
@@ -645,8 +702,8 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI :
NLPID_CCITT_ANSI_LMI)) {
- printk(KERN_INFO "%s: Received non-LMI frame with LMI"
- " DLCI\n", dev->name);
+ printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
+ dev->name);
return 1;
}
@@ -706,53 +763,53 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
}
i++;
- hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
+ state(hdlc)->rxseq = skb->data[i++]; /* TX sequence from peer */
rxseq = skb->data[i++]; /* Should confirm our sequence */
- txseq = hdlc->state.fr.txseq;
+ txseq = state(hdlc)->txseq;
if (dce)
- hdlc->state.fr.last_poll = jiffies;
+ state(hdlc)->last_poll = jiffies;
error = 0;
- if (!hdlc->state.fr.reliable)
+ if (!state(hdlc)->reliable)
error = 1;
- if (rxseq == 0 || rxseq != txseq) {
- hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */
+ if (rxseq == 0 || rxseq != txseq) { /* Ask for full report next time */
+ state(hdlc)->n391cnt = 0;
error = 1;
}
if (dce) {
- if (hdlc->state.fr.fullrep_sent && !error) {
+ if (state(hdlc)->fullrep_sent && !error) {
/* Stop sending full report - the last one has been confirmed by DTE */
- hdlc->state.fr.fullrep_sent = 0;
- pvc = hdlc->state.fr.first_pvc;
+ state(hdlc)->fullrep_sent = 0;
+ pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->state.new) {
pvc->state.new = 0;
/* Tell DTE that new PVC is now active */
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->dce_changed = 1;
}
pvc = pvc->next;
}
}
- if (hdlc->state.fr.dce_changed) {
+ if (state(hdlc)->dce_changed) {
reptype = LMI_FULLREP;
- hdlc->state.fr.fullrep_sent = 1;
- hdlc->state.fr.dce_changed = 0;
+ state(hdlc)->fullrep_sent = 1;
+ state(hdlc)->dce_changed = 0;
}
- hdlc->state.fr.request = 1; /* got request */
+ state(hdlc)->request = 1; /* got request */
fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0);
return 0;
}
/* DTE */
- hdlc->state.fr.request = 0; /* got response, no request pending */
+ state(hdlc)->request = 0; /* got response, no request pending */
if (error)
return 0;
@@ -760,7 +817,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (reptype != LMI_FULLREP)
return 0;
- pvc = hdlc->state.fr.first_pvc;
+ pvc = state(hdlc)->first_pvc;
while (pvc) {
pvc->state.deleted = 1;
@@ -827,7 +884,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
i += stat_len;
}
- pvc = hdlc->state.fr.first_pvc;
+ pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->state.deleted && pvc->state.exist) {
@@ -841,17 +898,16 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
}
/* Next full report after N391 polls */
- hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391;
+ state(hdlc)->n391cnt = state(hdlc)->settings.n391;
return 0;
}
-
static int fr_rx(struct sk_buff *skb)
{
- struct net_device *ndev = skb->dev;
- hdlc_device *hdlc = dev_to_hdlc(ndev);
+ struct net_device *frad = skb->dev;
+ hdlc_device *hdlc = dev_to_hdlc(frad);
fr_hdr *fh = (fr_hdr*)skb->data;
u8 *data = skb->data;
u16 dlci;
@@ -864,11 +920,11 @@ static int fr_rx(struct sk_buff *skb)
dlci = q922_to_dlci(skb->data);
if ((dlci == LMI_CCITT_ANSI_DLCI &&
- (hdlc->state.fr.settings.lmi == LMI_ANSI ||
- hdlc->state.fr.settings.lmi == LMI_CCITT)) ||
+ (state(hdlc)->settings.lmi == LMI_ANSI ||
+ state(hdlc)->settings.lmi == LMI_CCITT)) ||
(dlci == LMI_CISCO_DLCI &&
- hdlc->state.fr.settings.lmi == LMI_CISCO)) {
- if (fr_lmi_recv(ndev, skb))
+ state(hdlc)->settings.lmi == LMI_CISCO)) {
+ if (fr_lmi_recv(frad, skb))
goto rx_error;
dev_kfree_skb_any(skb);
return NET_RX_SUCCESS;
@@ -878,7 +934,7 @@ static int fr_rx(struct sk_buff *skb)
if (!pvc) {
#ifdef DEBUG_PKT
printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
- ndev->name, dlci);
+ frad->name, dlci);
#endif
dev_kfree_skb_any(skb);
return NET_RX_DROP;
@@ -886,7 +942,7 @@ static int fr_rx(struct sk_buff *skb)
if (pvc->state.fecn != fh->fecn) {
#ifdef DEBUG_ECN
- printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", ndev->name,
+ printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", frad->name,
dlci, fh->fecn ? "N" : "FF");
#endif
pvc->state.fecn ^= 1;
@@ -894,7 +950,7 @@ static int fr_rx(struct sk_buff *skb)
if (pvc->state.becn != fh->becn) {
#ifdef DEBUG_ECN
- printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", ndev->name,
+ printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", frad->name,
dlci, fh->becn ? "N" : "FF");
#endif
pvc->state.becn ^= 1;
@@ -902,7 +958,7 @@ static int fr_rx(struct sk_buff *skb)
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
- hdlc->stats.rx_dropped++;
+ dev_to_desc(frad)->stats.rx_dropped++;
return NET_RX_DROP;
}
@@ -938,13 +994,13 @@ static int fr_rx(struct sk_buff *skb)
default:
printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
- "PID=%x\n", ndev->name, oui, pid);
+ "PID=%x\n", frad->name, oui, pid);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
} else {
printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "
- "length = %i\n", ndev->name, data[3], skb->len);
+ "length = %i\n", frad->name, data[3], skb->len);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -964,7 +1020,7 @@ static int fr_rx(struct sk_buff *skb)
}
rx_error:
- hdlc->stats.rx_errors++; /* Mark error */
+ dev_to_desc(frad)->stats.rx_errors++; /* Mark error */
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -977,44 +1033,42 @@ static void fr_start(struct net_device *dev)
#ifdef DEBUG_LINK
printk(KERN_DEBUG "fr_start\n");
#endif
- if (hdlc->state.fr.settings.lmi != LMI_NONE) {
- hdlc->state.fr.reliable = 0;
- hdlc->state.fr.dce_changed = 1;
- hdlc->state.fr.request = 0;
- hdlc->state.fr.fullrep_sent = 0;
- hdlc->state.fr.last_errors = 0xFFFFFFFF;
- hdlc->state.fr.n391cnt = 0;
- hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0;
-
- init_timer(&hdlc->state.fr.timer);
+ if (state(hdlc)->settings.lmi != LMI_NONE) {
+ state(hdlc)->reliable = 0;
+ state(hdlc)->dce_changed = 1;
+ state(hdlc)->request = 0;
+ state(hdlc)->fullrep_sent = 0;
+ state(hdlc)->last_errors = 0xFFFFFFFF;
+ state(hdlc)->n391cnt = 0;
+ state(hdlc)->txseq = state(hdlc)->rxseq = 0;
+
+ init_timer(&state(hdlc)->timer);
/* First poll after 1 s */
- hdlc->state.fr.timer.expires = jiffies + HZ;
- hdlc->state.fr.timer.function = fr_timer;
- hdlc->state.fr.timer.data = (unsigned long)dev;
- add_timer(&hdlc->state.fr.timer);
+ state(hdlc)->timer.expires = jiffies + HZ;
+ state(hdlc)->timer.function = fr_timer;
+ state(hdlc)->timer.data = (unsigned long)dev;
+ add_timer(&state(hdlc)->timer);
} else
fr_set_link_state(1, dev);
}
-
static void fr_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
printk(KERN_DEBUG "fr_stop\n");
#endif
- if (hdlc->state.fr.settings.lmi != LMI_NONE)
- del_timer_sync(&hdlc->state.fr.timer);
+ if (state(hdlc)->settings.lmi != LMI_NONE)
+ del_timer_sync(&state(hdlc)->timer);
fr_set_link_state(0, dev);
}
-
static void fr_close(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- pvc_device *pvc = hdlc->state.fr.first_pvc;
+ pvc_device *pvc = state(hdlc)->first_pvc;
while (pvc) { /* Shutdown all PVCs for this FRAD */
if (pvc->main)
@@ -1025,7 +1079,8 @@ static void fr_close(struct net_device *dev)
}
}
-static void dlci_setup(struct net_device *dev)
+
+static void pvc_setup(struct net_device *dev)
{
dev->type = ARPHRD_DLCI;
dev->flags = IFF_POINTOPOINT;
@@ -1033,9 +1088,9 @@ static void dlci_setup(struct net_device *dev)
dev->addr_len = 2;
}
-static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
+static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
{
- hdlc_device *hdlc = dev_to_hdlc(master);
+ hdlc_device *hdlc = dev_to_hdlc(frad);
pvc_device *pvc = NULL;
struct net_device *dev;
int result, used;
@@ -1044,9 +1099,9 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
if (type == ARPHRD_ETHER)
prefix = "pvceth%d";
- if ((pvc = add_pvc(master, dlci)) == NULL) {
+ if ((pvc = add_pvc(frad, dlci)) == NULL) {
printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
- master->name);
+ frad->name);
return -ENOBUFS;
}
@@ -1060,11 +1115,11 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
"pvceth%d", ether_setup);
else
dev = alloc_netdev(sizeof(struct net_device_stats),
- "pvc%d", dlci_setup);
+ "pvc%d", pvc_setup);
if (!dev) {
printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
- master->name);
+ frad->name);
delete_unused_pvcs(hdlc);
return -ENOBUFS;
}
@@ -1102,8 +1157,8 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
dev->destructor = free_netdev;
*get_dev_p(pvc, type) = dev;
if (!used) {
- hdlc->state.fr.dce_changed = 1;
- hdlc->state.fr.dce_pvc_count++;
+ state(hdlc)->dce_changed = 1;
+ state(hdlc)->dce_pvc_count++;
}
return 0;
}
@@ -1128,8 +1183,8 @@ static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
*get_dev_p(pvc, type) = NULL;
if (!pvc_is_used(pvc)) {
- hdlc->state.fr.dce_pvc_count--;
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->dce_pvc_count--;
+ state(hdlc)->dce_changed = 1;
}
delete_unused_pvcs(hdlc);
return 0;
@@ -1137,14 +1192,13 @@ static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
-static void fr_destroy(hdlc_device *hdlc)
+static void fr_destroy(struct net_device *frad)
{
- pvc_device *pvc;
-
- pvc = hdlc->state.fr.first_pvc;
- hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
- hdlc->state.fr.dce_pvc_count = 0;
- hdlc->state.fr.dce_changed = 1;
+ hdlc_device *hdlc = dev_to_hdlc(frad);
+ pvc_device *pvc = state(hdlc)->first_pvc;
+ state(hdlc)->first_pvc = NULL; /* All PVCs destroyed */
+ state(hdlc)->dce_pvc_count = 0;
+ state(hdlc)->dce_changed = 1;
while (pvc) {
pvc_device *next = pvc->next;
@@ -1161,8 +1215,17 @@ static void fr_destroy(hdlc_device *hdlc)
}
+static struct hdlc_proto proto = {
+ .close = fr_close,
+ .start = fr_start,
+ .stop = fr_stop,
+ .detach = fr_destroy,
+ .ioctl = fr_ioctl,
+ .module = THIS_MODULE,
+};
+
-int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
+static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
{
fr_proto __user *fr_s = ifr->ifr_settings.ifs_ifsu.fr;
const size_t size = sizeof(fr_proto);
@@ -1173,12 +1236,14 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_FR;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(fr_s, &hdlc->state.fr.settings, size))
+ if (copy_to_user(fr_s, &state(hdlc)->settings, size))
return -EFAULT;
return 0;
@@ -1213,20 +1278,16 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- if (hdlc->proto.id != IF_PROTO_FR) {
- hdlc_proto_detach(hdlc);
- hdlc->state.fr.first_pvc = NULL;
- hdlc->state.fr.dce_pvc_count = 0;
+ if (dev_to_hdlc(dev)->proto != &proto) { /* Different proto */
+ result = attach_hdlc_protocol(dev, &proto, fr_rx,
+ sizeof(struct frad_state));
+ if (result)
+ return result;
+ state(hdlc)->first_pvc = NULL;
+ state(hdlc)->dce_pvc_count = 0;
}
- memcpy(&hdlc->state.fr.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.close = fr_close;
- hdlc->proto.start = fr_start;
- hdlc->proto.stop = fr_stop;
- hdlc->proto.detach = fr_destroy;
- hdlc->proto.netif_rx = fr_rx;
- hdlc->proto.id = IF_PROTO_FR;
+ memcpy(&state(hdlc)->settings, &new_settings, size);
+
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_FRAD;
@@ -1238,6 +1299,9 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
case IF_PROTO_FR_DEL_PVC:
case IF_PROTO_FR_ADD_ETH_PVC:
case IF_PROTO_FR_DEL_ETH_PVC:
+ if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */
+ return -EINVAL;
+
if(!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -1263,3 +1327,24 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Frame-Relay protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index fbaab5bf71e..e9f717070fd 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Point-to-point protocol support
*
- * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -22,6 +22,21 @@
#include <linux/lapb.h>
#include <linux/rtnetlink.h>
#include <linux/hdlc.h>
+#include <net/syncppp.h>
+
+struct ppp_state {
+ struct ppp_device pppdev;
+ struct ppp_device *syncppp_ptr;
+ int (*old_change_mtu)(struct net_device *dev, int new_mtu);
+};
+
+static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr);
+
+
+static inline struct ppp_state* state(hdlc_device *hdlc)
+{
+ return(struct ppp_state *)(hdlc->state);
+}
static int ppp_open(struct net_device *dev)
@@ -30,16 +45,16 @@ static int ppp_open(struct net_device *dev)
void *old_ioctl;
int result;
- dev->priv = &hdlc->state.ppp.syncppp_ptr;
- hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev;
- hdlc->state.ppp.pppdev.dev = dev;
+ dev->priv = &state(hdlc)->syncppp_ptr;
+ state(hdlc)->syncppp_ptr = &state(hdlc)->pppdev;
+ state(hdlc)->pppdev.dev = dev;
old_ioctl = dev->do_ioctl;
- hdlc->state.ppp.old_change_mtu = dev->change_mtu;
- sppp_attach(&hdlc->state.ppp.pppdev);
+ state(hdlc)->old_change_mtu = dev->change_mtu;
+ sppp_attach(&state(hdlc)->pppdev);
/* sppp_attach nukes them. We don't need syncppp's ioctl */
dev->do_ioctl = old_ioctl;
- hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO;
+ state(hdlc)->pppdev.sppp.pp_flags &= ~PP_CISCO;
dev->type = ARPHRD_PPP;
result = sppp_open(dev);
if (result) {
@@ -59,7 +74,7 @@ static void ppp_close(struct net_device *dev)
sppp_close(dev);
sppp_detach(dev);
dev->rebuild_header = NULL;
- dev->change_mtu = hdlc->state.ppp.old_change_mtu;
+ dev->change_mtu = state(hdlc)->old_change_mtu;
dev->mtu = HDLC_MAX_MTU;
dev->hard_header_len = 16;
}
@@ -73,13 +88,24 @@ static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev)
-int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .open = ppp_open,
+ .close = ppp_close,
+ .type_trans = ppp_type_trans,
+ .ioctl = ppp_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
int result;
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_PPP;
return 0; /* return protocol only, no settable parameters */
@@ -96,13 +122,10 @@ int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.open = ppp_open;
- hdlc->proto.close = ppp_close;
- hdlc->proto.type_trans = ppp_type_trans;
- hdlc->proto.id = IF_PROTO_PPP;
+ result = attach_hdlc_protocol(dev, &proto, NULL,
+ sizeof(struct ppp_state));
+ if (result)
+ return result;
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_PPP;
@@ -113,3 +136,25 @@ int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("PPP protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c
index f15aa6ba77f..fe3cae5c6b9 100644
--- a/drivers/net/wan/hdlc_raw.c
+++ b/drivers/net/wan/hdlc_raw.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* HDLC support
*
- * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -24,6 +24,8 @@
#include <linux/hdlc.h>
+static int raw_ioctl(struct net_device *dev, struct ifreq *ifr);
+
static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev)
{
return __constant_htons(ETH_P_IP);
@@ -31,7 +33,14 @@ static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev)
-int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .type_trans = raw_type_trans,
+ .ioctl = raw_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int raw_ioctl(struct net_device *dev, struct ifreq *ifr)
{
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
const size_t size = sizeof(raw_hdlc_proto);
@@ -41,12 +50,14 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_HDLC;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+ if (copy_to_user(raw_s, hdlc->state, size))
return -EFAULT;
return 0;
@@ -71,12 +82,11 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.type_trans = raw_type_trans;
- hdlc->proto.id = IF_PROTO_HDLC;
+ result = attach_hdlc_protocol(dev, &proto, NULL,
+ sizeof(raw_hdlc_proto));
+ if (result)
+ return result;
+ memcpy(hdlc->state, &new_settings, size);
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_RAWHDLC;
@@ -88,3 +98,25 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Raw HDLC protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c
index d1884987f94..1a69a9aaa9b 100644
--- a/drivers/net/wan/hdlc_raw_eth.c
+++ b/drivers/net/wan/hdlc_raw_eth.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* HDLC Ethernet emulation support
*
- * Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2002-2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -25,6 +25,7 @@
#include <linux/etherdevice.h>
#include <linux/hdlc.h>
+static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr);
static int eth_tx(struct sk_buff *skb, struct net_device *dev)
{
@@ -44,7 +45,14 @@ static int eth_tx(struct sk_buff *skb, struct net_device *dev)
}
-int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .type_trans = eth_type_trans,
+ .ioctl = raw_eth_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
{
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
const size_t size = sizeof(raw_hdlc_proto);
@@ -56,12 +64,14 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_HDLC_ETH;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+ if (copy_to_user(raw_s, hdlc->state, size))
return -EFAULT;
return 0;
@@ -86,12 +96,11 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.type_trans = eth_type_trans;
- hdlc->proto.id = IF_PROTO_HDLC_ETH;
+ result = attach_hdlc_protocol(dev, &proto, NULL,
+ sizeof(raw_hdlc_proto));
+ if (result)
+ return result;
+ memcpy(hdlc->state, &new_settings, size);
dev->hard_start_xmit = eth_tx;
old_ch_mtu = dev->change_mtu;
old_qlen = dev->tx_queue_len;
@@ -106,3 +115,25 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Ethernet encapsulation support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c
index a867fb411f8..e4bb9f8ad43 100644
--- a/drivers/net/wan/hdlc_x25.c
+++ b/drivers/net/wan/hdlc_x25.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* X.25 support
*
- * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -25,6 +25,8 @@
#include <net/x25device.h>
+static int x25_ioctl(struct net_device *dev, struct ifreq *ifr);
+
/* These functions are callbacks called by LAPB layer */
static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
@@ -162,30 +164,39 @@ static void x25_close(struct net_device *dev)
static int x25_rx(struct sk_buff *skb)
{
- hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+ struct hdlc_device_desc *desc = dev_to_desc(skb->dev);
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
- hdlc->stats.rx_dropped++;
+ desc->stats.rx_dropped++;
return NET_RX_DROP;
}
if (lapb_data_received(skb->dev, skb) == LAPB_OK)
return NET_RX_SUCCESS;
- hdlc->stats.rx_errors++;
+ desc->stats.rx_errors++;
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
+static struct hdlc_proto proto = {
+ .open = x25_open,
+ .close = x25_close,
+ .ioctl = x25_ioctl,
+ .module = THIS_MODULE,
+};
+
-int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
+static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
int result;
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_X25;
return 0; /* return protocol only, no settable parameters */
@@ -200,14 +211,9 @@ int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.open = x25_open;
- hdlc->proto.close = x25_close;
- hdlc->proto.netif_rx = x25_rx;
- hdlc->proto.type_trans = NULL;
- hdlc->proto.id = IF_PROTO_X25;
+ if ((result = attach_hdlc_protocol(dev, &proto,
+ x25_rx, 0)) != 0)
+ return result;
dev->hard_start_xmit = x25_xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_X25;
@@ -218,3 +224,25 @@ int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/pc300.h b/drivers/net/wan/pc300.h
index 2024b26b99e..63e9fcf31fb 100644
--- a/drivers/net/wan/pc300.h
+++ b/drivers/net/wan/pc300.h
@@ -100,6 +100,7 @@
#define _PC300_H
#include <linux/hdlc.h>
+#include <net/syncppp.h>
#include "hd64572.h"
#include "pc300-falc-lh.h"
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 56e69403d17..8d9b959bf15 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -2016,7 +2016,6 @@ static void sca_intr(pc300_t * card)
pc300ch_t *chan = &card->chan[ch];
pc300dev_t *d = &chan->d;
struct net_device *dev = d->dev;
- hdlc_device *hdlc = dev_to_hdlc(dev);
spin_lock(&card->card_lock);
@@ -2049,8 +2048,8 @@ static void sca_intr(pc300_t * card)
}
cpc_net_rx(dev);
/* Discard invalid frames */
- hdlc->stats.rx_errors++;
- hdlc->stats.rx_over_errors++;
+ hdlc_stats(dev)->rx_errors++;
+ hdlc_stats(dev)->rx_over_errors++;
chan->rx_first_bd = 0;
chan->rx_last_bd = N_DMA_RX_BUF - 1;
rx_dma_start(card, ch);
@@ -2116,8 +2115,8 @@ static void sca_intr(pc300_t * card)
card->hw.cpld_reg2) &
~ (CPLD_REG2_FALC_LED1 << (2 * ch)));
}
- hdlc->stats.tx_errors++;
- hdlc->stats.tx_fifo_errors++;
+ hdlc_stats(dev)->tx_errors++;
+ hdlc_stats(dev)->tx_fifo_errors++;
sca_tx_intr(d);
}
}
@@ -2534,7 +2533,6 @@ static int cpc_change_mtu(struct net_device *dev, int new_mtu)
static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
pc300dev_t *d = (pc300dev_t *) dev->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
@@ -2552,10 +2550,10 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGPC300CONF:
#ifdef CONFIG_PC300_MLPPP
if (conf->proto != PC300_PROTO_MLPPP) {
- conf->proto = hdlc->proto.id;
+ conf->proto = /* FIXME hdlc->proto.id */ 0;
}
#else
- conf->proto = hdlc->proto.id;
+ conf->proto = /* FIXME hdlc->proto.id */ 0;
#endif
memcpy(&conf_aux.conf, conf, sizeof(pc300chconf_t));
memcpy(&conf_aux.hw, &card->hw, sizeof(pc300hw_t));
@@ -2588,12 +2586,12 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
} else {
memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t));
- hdlc->proto.id = conf->proto;
+ /* FIXME hdlc->proto.id = conf->proto; */
}
}
#else
memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t));
- hdlc->proto.id = conf->proto;
+ /* FIXME hdlc->proto.id = conf->proto; */
#endif
return 0;
case SIOCGPC300STATUS:
@@ -2606,7 +2604,7 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGPC300UTILSTATS:
{
if (!arg) { /* clear statistics */
- memset(&hdlc->stats, 0, sizeof(struct net_device_stats));
+ memset(hdlc_stats(dev), 0, sizeof(struct net_device_stats));
if (card->hw.type == PC300_TE) {
memset(&chan->falc, 0, sizeof(falc_t));
}
@@ -2617,7 +2615,7 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
pc300stats.hw_type = card->hw.type;
pc300stats.line_on = card->chan[ch].d.line_on;
pc300stats.line_off = card->chan[ch].d.line_off;
- memcpy(&pc300stats.gen_stats, &hdlc->stats,
+ memcpy(&pc300stats.gen_stats, hdlc_stats(dev),
sizeof(struct net_device_stats));
if (card->hw.type == PC300_TE)
memcpy(&pc300stats.te_stats,&chan->falc,sizeof(falc_t));
@@ -3147,7 +3145,6 @@ static void cpc_closech(pc300dev_t * d)
int cpc_open(struct net_device *dev)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
pc300dev_t *d = (pc300dev_t *) dev->priv;
struct ifreq ifr;
int result;
@@ -3156,12 +3153,14 @@ int cpc_open(struct net_device *dev)
printk("pc300: cpc_open");
#endif
+#ifdef FIXME
if (hdlc->proto.id == IF_PROTO_PPP) {
d->if_ptr = &hdlc->state.ppp.pppdev;
}
+#endif
result = hdlc_open(dev);
- if (hdlc->proto.id == IF_PROTO_PPP) {
+ if (/* FIXME hdlc->proto.id == IF_PROTO_PPP*/ 0) {
dev->priv = d;
}
if (result) {
@@ -3176,7 +3175,6 @@ int cpc_open(struct net_device *dev)
static int cpc_close(struct net_device *dev)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
pc300dev_t *d = (pc300dev_t *) dev->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
@@ -3193,7 +3191,7 @@ static int cpc_close(struct net_device *dev)
CPC_UNLOCK(card, flags);
hdlc_close(dev);
- if (hdlc->proto.id == IF_PROTO_PPP) {
+ if (/* FIXME hdlc->proto.id == IF_PROTO_PPP*/ 0) {
d->if_ptr = NULL;
}
#ifdef CONFIG_PC300_MLPPP
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index c13b459a013..218f7b574ab 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -469,7 +469,7 @@ static void sppp_lcp_input (struct sppp *sp, struct sk_buff *skb)
struct net_device *dev = sp->pp_if;
int len = skb->len;
u8 *p, opt[6];
- u32 rmagic;
+ u32 rmagic = 0;
if (!pskb_may_pull(skb, sizeof(struct lcp_header))) {
if (sp->pp_flags & PP_DEBUG)
@@ -763,7 +763,7 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
{
struct in_device *in_dev;
struct in_ifaddr *ifa;
- u32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */
+ __be32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */
#ifdef CONFIG_INET
rcu_read_lock();
if ((in_dev = __in_dev_get_rcu(dev)) != NULL)
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index bff04cba3fe..ba737c6cebe 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -5868,7 +5868,7 @@ static int airo_set_essid(struct net_device *dev,
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
/* Check the size of the string */
- if(dwrq->length > IW_ESSID_MAX_SIZE+1) {
+ if(dwrq->length > IW_ESSID_MAX_SIZE) {
return -E2BIG ;
}
/* Check if index is valid */
@@ -5880,7 +5880,7 @@ static int airo_set_essid(struct net_device *dev,
memset(SSID_rid.ssids[index].ssid, 0,
sizeof(SSID_rid.ssids[index].ssid));
memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
- SSID_rid.ssids[index].len = dwrq->length - 1;
+ SSID_rid.ssids[index].len = dwrq->length;
}
SSID_rid.len = sizeof(SSID_rid);
/* Write it to the card */
@@ -5990,7 +5990,7 @@ static int airo_set_nick(struct net_device *dev,
struct airo_info *local = dev->priv;
/* Check the size of the string */
- if(dwrq->length > 16 + 1) {
+ if(dwrq->length > 16) {
return -E2BIG;
}
readConfigRid(local, 1);
@@ -6015,7 +6015,7 @@ static int airo_get_nick(struct net_device *dev,
readConfigRid(local, 1);
strncpy(extra, local->config.nodeName, 16);
extra[16] = '\0';
- dwrq->length = strlen(extra) + 1;
+ dwrq->length = strlen(extra);
return 0;
}
@@ -6767,9 +6767,9 @@ static int airo_set_retry(struct net_device *dev,
}
readConfigRid(local, 1);
if(vwrq->flags & IW_RETRY_LIMIT) {
- if(vwrq->flags & IW_RETRY_MAX)
+ if(vwrq->flags & IW_RETRY_LONG)
local->config.longRetryLimit = vwrq->value;
- else if (vwrq->flags & IW_RETRY_MIN)
+ else if (vwrq->flags & IW_RETRY_SHORT)
local->config.shortRetryLimit = vwrq->value;
else {
/* No modifier : set both */
@@ -6805,14 +6805,14 @@ static int airo_get_retry(struct net_device *dev,
if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
vwrq->flags = IW_RETRY_LIFETIME;
vwrq->value = (int)local->config.txLifetime * 1024;
- } else if((vwrq->flags & IW_RETRY_MAX)) {
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ } else if((vwrq->flags & IW_RETRY_LONG)) {
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
vwrq->value = (int)local->config.longRetryLimit;
} else {
vwrq->flags = IW_RETRY_LIMIT;
vwrq->value = (int)local->config.shortRetryLimit;
if((int)local->config.shortRetryLimit != (int)local->config.longRetryLimit)
- vwrq->flags |= IW_RETRY_MIN;
+ vwrq->flags |= IW_RETRY_SHORT;
}
return 0;
@@ -6990,6 +6990,7 @@ static int airo_set_power(struct net_device *dev,
local->config.rmode |= RXMODE_BC_MC_ADDR;
set_bit (FLAG_COMMIT, &local->flags);
case IW_POWER_ON:
+ /* This is broken, fixme ;-) */
break;
default:
return -EINVAL;
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 995c7bea589..0fc267d626d 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -1656,13 +1656,13 @@ static int atmel_set_essid(struct net_device *dev,
priv->connect_to_any_BSS = 0;
/* Check the size of the string */
- if (dwrq->length > MAX_SSID_LENGTH + 1)
+ if (dwrq->length > MAX_SSID_LENGTH)
return -E2BIG;
if (index != 0)
return -EINVAL;
- memcpy(priv->new_SSID, extra, dwrq->length - 1);
- priv->new_SSID_size = dwrq->length - 1;
+ memcpy(priv->new_SSID, extra, dwrq->length);
+ priv->new_SSID_size = dwrq->length;
}
return -EINPROGRESS;
@@ -2120,9 +2120,9 @@ static int atmel_set_retry(struct net_device *dev,
struct atmel_private *priv = netdev_priv(dev);
if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) {
- if (vwrq->flags & IW_RETRY_MAX)
+ if (vwrq->flags & IW_RETRY_LONG)
priv->long_retry = vwrq->value;
- else if (vwrq->flags & IW_RETRY_MIN)
+ else if (vwrq->flags & IW_RETRY_SHORT)
priv->short_retry = vwrq->value;
else {
/* No modifier : set both */
@@ -2144,15 +2144,15 @@ static int atmel_get_retry(struct net_device *dev,
vwrq->disabled = 0; /* Can't be disabled */
- /* Note : by default, display the min retry number */
- if (vwrq->flags & IW_RETRY_MAX) {
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ /* Note : by default, display the short retry number */
+ if (vwrq->flags & IW_RETRY_LONG) {
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
vwrq->value = priv->long_retry;
} else {
vwrq->flags = IW_RETRY_LIMIT;
vwrq->value = priv->short_retry;
if (priv->long_retry != priv->short_retry)
- vwrq->flags |= IW_RETRY_MIN;
+ vwrq->flags |= IW_RETRY_SHORT;
}
return 0;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index 6d4ea36bc56..d6a8bf09878 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -666,7 +666,6 @@ struct bcm43xx_noise_calculation {
};
struct bcm43xx_stats {
- u8 link_quality;
u8 noise;
struct iw_statistics wstats;
/* Store the last TX/RX times here for updating the leds. */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
index 923275ea078..b9df06a06ea 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
@@ -54,7 +54,7 @@ static ssize_t write_file_dummy(struct file *file, const char __user *buf,
static int open_file_generic(struct inode *inode, struct file *file)
{
- file->private_data = inode->u.generic_ip;
+ file->private_data = inode->i_private;
return 0;
}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index cb9a3ae8463..eb65db7393b 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -2405,9 +2405,10 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm)
BCM43xx_UCODE_TIME) & 0x1f);
if ( value16 > 0x128 ) {
- dprintk(KERN_ERR PFX
- "Firmware: no support for microcode rev > 0x128\n");
- err = -1;
+ printk(KERN_ERR PFX
+ "Firmware: no support for microcode extracted "
+ "from version 4.x binary drivers.\n");
+ err = -EOPNOTSUPP;
goto err_release_fw;
}
@@ -3169,8 +3170,7 @@ static void bcm43xx_periodic_work_handler(void *d)
* be preemtible.
*/
mutex_lock(&bcm->mutex);
- netif_stop_queue(bcm->net_dev);
- synchronize_net();
+ netif_tx_disable(bcm->net_dev);
spin_lock_irqsave(&bcm->irq_lock, flags);
bcm43xx_mac_suspend(bcm);
if (bcm43xx_using_pio(bcm))
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
index eafd0f66268..52ce2a9334f 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
@@ -361,7 +361,7 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm)
if (phy->rev <= 2)
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg1[i]);
- else if ((phy->rev == 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200))
+ else if ((phy->rev >= 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200))
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg3[i]);
else
@@ -371,7 +371,7 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm)
if (phy->rev == 2)
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
- else if ((phy->rev > 2) && (phy->rev <= 7))
+ else if ((phy->rev > 2) && (phy->rev <= 8))
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr2[i]);
@@ -1197,7 +1197,7 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm)
if (phy->rev == 1)
bcm43xx_phy_initb5(bcm);
- else if (phy->rev >= 2 && phy->rev <= 7)
+ else
bcm43xx_phy_initb6(bcm);
if (phy->rev >= 2 || phy->connected)
bcm43xx_phy_inita(bcm);
@@ -1241,23 +1241,22 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm)
bcm43xx_phy_lo_g_measure(bcm);
} else {
if (radio->version == 0x2050 && radio->revision == 8) {
- //FIXME
+ bcm43xx_radio_write16(bcm, 0x0052,
+ (radio->txctl1 << 4) | radio->txctl2);
} else {
bcm43xx_radio_write16(bcm, 0x0052,
(bcm43xx_radio_read16(bcm, 0x0052)
& 0xFFF0) | radio->txctl1);
}
if (phy->rev >= 6) {
- /*
bcm43xx_phy_write(bcm, 0x0036,
(bcm43xx_phy_read(bcm, 0x0036)
- & 0xF000) | (FIXME << 12));
- */
+ & 0xF000) | (radio->txctl2 << 12));
}
if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
bcm43xx_phy_write(bcm, 0x002E, 0x8075);
else
- bcm43xx_phy_write(bcm, 0x003E, 0x807F);
+ bcm43xx_phy_write(bcm, 0x002E, 0x807F);
if (phy->rev < 2)
bcm43xx_phy_write(bcm, 0x002F, 0x0101);
else
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
index 888077fc14c..9b7b15cf656 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
@@ -334,7 +334,7 @@ static int bcm43xx_wx_get_nick(struct net_device *net_dev,
size_t len;
mutex_lock(&bcm->mutex);
- len = strlen(bcm->nick) + 1;
+ len = strlen(bcm->nick);
memcpy(extra, bcm->nick, len);
data->data.length = (__u16)len;
data->data.flags = 1;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
index c0efbfe605a..0159e4e9320 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
@@ -496,15 +496,14 @@ int bcm43xx_rx(struct bcm43xx_private *bcm,
stats.signal = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm,
!!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ),
!!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ));
-//TODO stats.noise =
+ stats.noise = bcm->stats.noise;
if (is_ofdm)
stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp);
else
stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp);
stats.received_channel = radio->channel;
-//TODO stats.control =
stats.mask = IEEE80211_STATMASK_SIGNAL |
-//TODO IEEE80211_STATMASK_NOISE |
+ IEEE80211_STATMASK_NOISE |
IEEE80211_STATMASK_RATE |
IEEE80211_STATMASK_RSSI;
if (phy->type == BCM43xx_PHYTYPE_A)
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
index 7a4978516ea..d061fb3443f 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/hostap/hostap_ioctl.c
@@ -1412,9 +1412,9 @@ static int prism2_ioctl_siwretry(struct net_device *dev,
/* what could be done, if firmware would support this.. */
if (rrq->flags & IW_RETRY_LIMIT) {
- if (rrq->flags & IW_RETRY_MAX)
+ if (rrq->flags & IW_RETRY_LONG)
HFA384X_RID_LONGRETRYLIMIT = rrq->value;
- else if (rrq->flags & IW_RETRY_MIN)
+ else if (rrq->flags & IW_RETRY_SHORT)
HFA384X_RID_SHORTRETRYLIMIT = rrq->value;
else {
HFA384X_RID_LONGRETRYLIMIT = rrq->value;
@@ -1468,14 +1468,14 @@ static int prism2_ioctl_giwretry(struct net_device *dev,
rrq->value = le16_to_cpu(altretry);
else
rrq->value = local->manual_retry_count;
- } else if ((rrq->flags & IW_RETRY_MAX)) {
- rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ } else if ((rrq->flags & IW_RETRY_LONG)) {
+ rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
rrq->value = longretry;
} else {
rrq->flags = IW_RETRY_LIMIT;
rrq->value = shortretry;
if (shortretry != longretry)
- rrq->flags |= IW_RETRY_MIN;
+ rrq->flags |= IW_RETRY_SHORT;
}
}
return 0;
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
index b4d81a04c89..6c5add701a6 100644
--- a/drivers/net/wireless/ipw2100.c
+++ b/drivers/net/wireless/ipw2100.c
@@ -6958,7 +6958,7 @@ static int ipw2100_wx_set_essid(struct net_device *dev,
}
if (wrqu->essid.flags && wrqu->essid.length) {
- length = wrqu->essid.length - 1;
+ length = wrqu->essid.length;
essid = extra;
}
@@ -7051,7 +7051,7 @@ static int ipw2100_wx_get_nick(struct net_device *dev,
struct ipw2100_priv *priv = ieee80211_priv(dev);
- wrqu->data.length = strlen(priv->nick) + 1;
+ wrqu->data.length = strlen(priv->nick);
memcpy(extra, priv->nick, wrqu->data.length);
wrqu->data.flags = 1; /* active */
@@ -7343,14 +7343,14 @@ static int ipw2100_wx_set_retry(struct net_device *dev,
goto done;
}
- if (wrqu->retry.flags & IW_RETRY_MIN) {
+ if (wrqu->retry.flags & IW_RETRY_SHORT) {
err = ipw2100_set_short_retry(priv, wrqu->retry.value);
IPW_DEBUG_WX("SET Short Retry Limit -> %d \n",
wrqu->retry.value);
goto done;
}
- if (wrqu->retry.flags & IW_RETRY_MAX) {
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
err = ipw2100_set_long_retry(priv, wrqu->retry.value);
IPW_DEBUG_WX("SET Long Retry Limit -> %d \n",
wrqu->retry.value);
@@ -7383,14 +7383,14 @@ static int ipw2100_wx_get_retry(struct net_device *dev,
if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
return -EINVAL;
- if (wrqu->retry.flags & IW_RETRY_MAX) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
wrqu->retry.value = priv->long_retry_limit;
} else {
wrqu->retry.flags =
(priv->short_retry_limit !=
priv->long_retry_limit) ?
- IW_RETRY_LIMIT | IW_RETRY_MIN : IW_RETRY_LIMIT;
+ IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
wrqu->retry.value = priv->short_retry_limit;
}
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index 7358664e090..5685d7ba55b 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -8875,8 +8875,6 @@ static int ipw_wx_set_essid(struct net_device *dev,
}
length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE);
- if (!extra[length - 1])
- length--;
priv->config |= CFG_STATIC_ESSID;
@@ -8953,7 +8951,7 @@ static int ipw_wx_get_nick(struct net_device *dev,
struct ipw_priv *priv = ieee80211_priv(dev);
IPW_DEBUG_WX("Getting nick\n");
mutex_lock(&priv->mutex);
- wrqu->data.length = strlen(priv->nick) + 1;
+ wrqu->data.length = strlen(priv->nick);
memcpy(extra, priv->nick, wrqu->data.length);
wrqu->data.flags = 1; /* active */
mutex_unlock(&priv->mutex);
@@ -9276,9 +9274,9 @@ static int ipw_wx_set_retry(struct net_device *dev,
return -EINVAL;
mutex_lock(&priv->mutex);
- if (wrqu->retry.flags & IW_RETRY_MIN)
+ if (wrqu->retry.flags & IW_RETRY_SHORT)
priv->short_retry_limit = (u8) wrqu->retry.value;
- else if (wrqu->retry.flags & IW_RETRY_MAX)
+ else if (wrqu->retry.flags & IW_RETRY_LONG)
priv->long_retry_limit = (u8) wrqu->retry.value;
else {
priv->short_retry_limit = (u8) wrqu->retry.value;
@@ -9307,11 +9305,11 @@ static int ipw_wx_get_retry(struct net_device *dev,
return -EINVAL;
}
- if (wrqu->retry.flags & IW_RETRY_MAX) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
wrqu->retry.value = priv->long_retry_limit;
- } else if (wrqu->retry.flags & IW_RETRY_MIN) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ } else if (wrqu->retry.flags & IW_RETRY_SHORT) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
wrqu->retry.value = priv->short_retry_limit;
} else {
wrqu->retry.flags = IW_RETRY_LIMIT;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 1840b69e3cb..9e19a963feb 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -3037,7 +3037,7 @@ static int orinoco_ioctl_getessid(struct net_device *dev,
}
erq->flags = 1;
- erq->length = strlen(essidbuf) + 1;
+ erq->length = strlen(essidbuf);
return 0;
}
@@ -3078,7 +3078,7 @@ static int orinoco_ioctl_getnick(struct net_device *dev,
memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1);
orinoco_unlock(priv, &flags);
- nrq->length = strlen(nickbuf)+1;
+ nrq->length = strlen(nickbuf);
return 0;
}
@@ -3575,14 +3575,14 @@ static int orinoco_ioctl_getretry(struct net_device *dev,
rrq->value = lifetime * 1000; /* ??? */
} else {
/* By default, display the min number */
- if ((rrq->flags & IW_RETRY_MAX)) {
- rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if ((rrq->flags & IW_RETRY_LONG)) {
+ rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
rrq->value = long_limit;
} else {
rrq->flags = IW_RETRY_LIMIT;
rrq->value = short_limit;
if(short_limit != long_limit)
- rrq->flags |= IW_RETRY_MIN;
+ rrq->flags |= IW_RETRY_SHORT;
}
}
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index c09fbf733b3..286325ca329 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -742,9 +742,9 @@ prism54_set_essid(struct net_device *ndev, struct iw_request_info *info,
/* Check if we were asked for `any' */
if (dwrq->flags && dwrq->length) {
- if (dwrq->length > min(33, IW_ESSID_MAX_SIZE + 1))
+ if (dwrq->length > 32)
return -E2BIG;
- essid.length = dwrq->length - 1;
+ essid.length = dwrq->length;
memcpy(essid.octets, extra, dwrq->length);
} else
essid.length = 0;
@@ -814,7 +814,7 @@ prism54_get_nick(struct net_device *ndev, struct iw_request_info *info,
dwrq->length = 0;
down_read(&priv->mib_sem);
- dwrq->length = strlen(priv->nickname) + 1;
+ dwrq->length = strlen(priv->nickname);
memcpy(extra, priv->nickname, dwrq->length);
up_read(&priv->mib_sem);
@@ -992,9 +992,9 @@ prism54_set_retry(struct net_device *ndev, struct iw_request_info *info,
return -EINVAL;
if (vwrq->flags & IW_RETRY_LIMIT) {
- if (vwrq->flags & IW_RETRY_MIN)
+ if (vwrq->flags & IW_RETRY_SHORT)
slimit = vwrq->value;
- else if (vwrq->flags & IW_RETRY_MAX)
+ else if (vwrq->flags & IW_RETRY_LONG)
llimit = vwrq->value;
else {
/* we are asked to set both */
@@ -1035,18 +1035,18 @@ prism54_get_retry(struct net_device *ndev, struct iw_request_info *info,
mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r);
vwrq->value = r.u * 1024;
vwrq->flags = IW_RETRY_LIFETIME;
- } else if ((vwrq->flags & IW_RETRY_MAX)) {
+ } else if ((vwrq->flags & IW_RETRY_LONG)) {
/* we are asked for the long retry limit */
rvalue |=
mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r);
vwrq->value = r.u;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
} else {
/* default. get the short retry limit */
rvalue |=
mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r);
vwrq->value = r.u;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
}
return rvalue;
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 4574290f971..e82548ea609 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -1173,7 +1173,7 @@ static int ray_set_essid(struct net_device *dev,
return -EOPNOTSUPP;
} else {
/* Check the size of the string */
- if(dwrq->length > IW_ESSID_MAX_SIZE + 1) {
+ if(dwrq->length > IW_ESSID_MAX_SIZE) {
return -E2BIG;
}
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index ccaf28e8db0..337c692f6fd 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -1342,7 +1342,7 @@ static unsigned char *strip_make_packet(unsigned char *buffer,
* 'broadcast hub' radio (First byte of address being 0xFF means broadcast)
*/
if (haddr.c[0] == 0xFF) {
- u32 brd = 0;
+ __be32 brd = 0;
struct in_device *in_dev;
rcu_read_lock();
@@ -1406,7 +1406,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
int doreset = (long) jiffies - strip_info->watchdog_doreset >= 0;
int doprobe = (long) jiffies - strip_info->watchdog_doprobe >= 0
&& !doreset;
- u32 addr, brd;
+ __be32 addr, brd;
/*
* 1. If we have a packet, encapsulate it and put it in the buffer
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index e0d294c1297..e3ae5f60d5b 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -1802,15 +1802,15 @@ static int wl3501_get_retry(struct net_device *dev,
&retry, sizeof(retry));
if (rc)
goto out;
- if (wrqu->retry.flags & IW_RETRY_MAX) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
goto set_value;
}
rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_SHORT_RETRY_LIMIT,
&retry, sizeof(retry));
if (rc)
goto out;
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
set_value:
wrqu->retry.value = retry;
wrqu->retry.disabled = 0;
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index c52e9bcf8d0..80af9a9fcbb 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -119,7 +119,7 @@ static void zd1201_usbfree(struct urb *urb, struct pt_regs *regs)
switch(urb->status) {
case -EILSEQ:
case -ENODEV:
- case -ETIMEDOUT:
+ case -ETIME:
case -ENOENT:
case -EPIPE:
case -EOVERFLOW:
@@ -201,7 +201,7 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
switch(urb->status) {
case -EILSEQ:
case -ENODEV:
- case -ETIMEDOUT:
+ case -ETIME:
case -ENOENT:
case -EPIPE:
case -EOVERFLOW:
@@ -1218,7 +1218,7 @@ static int zd1201_set_essid(struct net_device *dev,
return -EINVAL;
if (data->length < 1)
data->length = 1;
- zd->essidlen = data->length-1;
+ zd->essidlen = data->length;
memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1);
memcpy(zd->essid, essid, data->length);
return zd1201_join(zd, zd->essid, zd->essidlen);
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 7c4e32cf0d4..aa661b2b76c 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -249,7 +249,6 @@ int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_ioread16_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -260,7 +259,6 @@ int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_ioread32_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -271,7 +269,6 @@ int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_iowrite16_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -282,7 +279,6 @@ int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_iowrite32_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -294,7 +290,6 @@ int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_ioread32v_locked(chip, values, addresses, count);
mutex_unlock(&chip->mutex);
@@ -306,7 +301,6 @@ int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_iowrite32a_locked(chip, ioreqs, count);
mutex_unlock(&chip->mutex);
@@ -331,13 +325,22 @@ static int read_pod(struct zd_chip *chip, u8 *rf_type)
chip->patch_cr157 = (value >> 13) & 0x1;
chip->patch_6m_band_edge = (value >> 21) & 0x1;
chip->new_phy_layout = (value >> 31) & 0x1;
+ chip->link_led = ((value >> 4) & 1) ? LED1 : LED2;
+ chip->supports_tx_led = 1;
+ if (value & (1 << 24)) { /* LED scenario */
+ if (value & (1 << 29))
+ chip->supports_tx_led = 0;
+ }
dev_dbg_f(zd_chip_dev(chip),
"RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d "
- "patch 6M %d new PHY %d\n",
+ "patch 6M %d new PHY %d link LED%d tx led %d\n",
zd_rf_name(*rf_type), *rf_type,
chip->pa_type, chip->patch_cck_gain,
- chip->patch_cr157, chip->patch_6m_band_edge, chip->new_phy_layout);
+ chip->patch_cr157, chip->patch_6m_band_edge,
+ chip->new_phy_layout,
+ chip->link_led == LED1 ? 1 : 2,
+ chip->supports_tx_led);
return 0;
error:
*rf_type = 0;
@@ -1181,7 +1184,7 @@ static int update_pwr_int(struct zd_chip *chip, u8 channel)
u8 value = chip->pwr_int_values[channel - 1];
dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_int %#04x\n",
channel, value);
- return zd_iowrite32_locked(chip, value, CR31);
+ return zd_iowrite16_locked(chip, value, CR31);
}
static int update_pwr_cal(struct zd_chip *chip, u8 channel)
@@ -1189,12 +1192,12 @@ static int update_pwr_cal(struct zd_chip *chip, u8 channel)
u8 value = chip->pwr_cal_values[channel-1];
dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_cal %#04x\n",
channel, value);
- return zd_iowrite32_locked(chip, value, CR68);
+ return zd_iowrite16_locked(chip, value, CR68);
}
static int update_ofdm_cal(struct zd_chip *chip, u8 channel)
{
- struct zd_ioreq32 ioreqs[3];
+ struct zd_ioreq16 ioreqs[3];
ioreqs[0].addr = CR67;
ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1];
@@ -1206,7 +1209,7 @@ static int update_ofdm_cal(struct zd_chip *chip, u8 channel)
dev_dbg_f(zd_chip_dev(chip),
"channel %d ofdm_cal 36M %#04x 48M %#04x 54M %#04x\n",
channel, ioreqs[0].value, ioreqs[1].value, ioreqs[2].value);
- return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
}
static int update_channel_integration_and_calibration(struct zd_chip *chip,
@@ -1218,7 +1221,7 @@ static int update_channel_integration_and_calibration(struct zd_chip *chip,
if (r)
return r;
if (chip->is_zd1211b) {
- static const struct zd_ioreq32 ioreqs[] = {
+ static const struct zd_ioreq16 ioreqs[] = {
{ CR69, 0x28 },
{},
{ CR69, 0x2a },
@@ -1230,7 +1233,7 @@ static int update_channel_integration_and_calibration(struct zd_chip *chip,
r = update_pwr_cal(chip, channel);
if (r)
return r;
- r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
if (r)
return r;
}
@@ -1252,7 +1255,7 @@ static int patch_cck_gain(struct zd_chip *chip)
if (r)
return r;
dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff);
- return zd_iowrite32_locked(chip, value & 0xff, CR47);
+ return zd_iowrite16_locked(chip, value & 0xff, CR47);
}
int zd_chip_set_channel(struct zd_chip *chip, u8 channel)
@@ -1295,89 +1298,60 @@ u8 zd_chip_get_channel(struct zd_chip *chip)
return channel;
}
-static u16 led_mask(int led)
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status)
{
- switch (led) {
- case 1:
- return LED1;
- case 2:
- return LED2;
- default:
- return 0;
- }
-}
-
-static int read_led_reg(struct zd_chip *chip, u16 *status)
-{
- ZD_ASSERT(mutex_is_locked(&chip->mutex));
- return zd_ioread16_locked(chip, status, CR_LED);
-}
-
-static int write_led_reg(struct zd_chip *chip, u16 status)
-{
- ZD_ASSERT(mutex_is_locked(&chip->mutex));
- return zd_iowrite16_locked(chip, status, CR_LED);
-}
+ static const zd_addr_t a[] = {
+ FW_LINK_STATUS,
+ CR_LED,
+ };
-int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status)
-{
- int r, ret;
- u16 mask = led_mask(led);
- u16 reg;
+ int r;
+ u16 v[ARRAY_SIZE(a)];
+ struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = {
+ [0] = { FW_LINK_STATUS },
+ [1] = { CR_LED },
+ };
+ u16 other_led;
- if (!mask)
- return -EINVAL;
mutex_lock(&chip->mutex);
- r = read_led_reg(chip, &reg);
+ r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a));
if (r)
- return r;
+ goto out;
+
+ other_led = chip->link_led == LED1 ? LED2 : LED1;
+
switch (status) {
- case LED_STATUS:
- return (reg & mask) ? LED_ON : LED_OFF;
case LED_OFF:
- reg &= ~mask;
- ret = LED_OFF;
+ ioreqs[0].value = FW_LINK_OFF;
+ ioreqs[1].value = v[1] & ~(LED1|LED2);
break;
- case LED_FLIP:
- reg ^= mask;
- ret = (reg&mask) ? LED_ON : LED_OFF;
+ case LED_SCANNING:
+ ioreqs[0].value = FW_LINK_OFF;
+ ioreqs[1].value = v[1] & ~other_led;
+ if (get_seconds() % 3 == 0) {
+ ioreqs[1].value &= ~chip->link_led;
+ } else {
+ ioreqs[1].value |= chip->link_led;
+ }
break;
- case LED_ON:
- reg |= mask;
- ret = LED_ON;
+ case LED_ASSOCIATED:
+ ioreqs[0].value = FW_LINK_TX;
+ ioreqs[1].value = v[1] & ~other_led;
+ ioreqs[1].value |= chip->link_led;
break;
default:
- return -EINVAL;
- }
- r = write_led_reg(chip, reg);
- if (r) {
- ret = r;
+ r = -EINVAL;
goto out;
}
-out:
- mutex_unlock(&chip->mutex);
- return r;
-}
-
-int zd_chip_led_flip(struct zd_chip *chip, int led,
- const unsigned int *phases_msecs, unsigned int count)
-{
- int i, r;
- enum led_status status;
- r = zd_chip_led_status(chip, led, LED_STATUS);
- if (r)
- return r;
- status = r;
- for (i = 0; i < count; i++) {
- r = zd_chip_led_status(chip, led, LED_FLIP);
- if (r < 0)
+ if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) {
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
goto out;
- msleep(phases_msecs[i]);
}
-
+ r = 0;
out:
- zd_chip_led_status(chip, led, status);
+ mutex_unlock(&chip->mutex);
return r;
}
@@ -1679,4 +1653,3 @@ int zd_rfwritev_cr_locked(struct zd_chip *chip,
return 0;
}
-
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index 4b125085989..ae59597ce4e 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -428,6 +428,7 @@
/* masks for controlling LEDs */
#define LED1 0x0100
#define LED2 0x0200
+#define LED_SW 0x0400
/* Seems to indicate that the configuration is over.
*/
@@ -629,6 +630,10 @@
#define FW_SOFT_RESET FW_REG(4)
#define FW_FLASH_CHK FW_REG(5)
+#define FW_LINK_OFF 0x0
+#define FW_LINK_TX 0x1
+/* 0x2 - link led on? */
+
enum {
CR_BASE_OFFSET = 0x9000,
FW_START_OFFSET = 0xee00,
@@ -663,8 +668,11 @@ struct zd_chip {
u8 pwr_int_values[E2P_CHANNEL_COUNT];
/* SetPointOFDM in the vendor driver */
u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT];
- u8 pa_type:4, patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
- new_phy_layout:1, is_zd1211b:1;
+ u16 link_led;
+ unsigned int pa_type:4,
+ patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
+ new_phy_layout:1,
+ is_zd1211b:1, supports_tx_led:1;
};
static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb)
@@ -812,15 +820,12 @@ int zd_chip_lock_phy_regs(struct zd_chip *chip);
int zd_chip_unlock_phy_regs(struct zd_chip *chip);
enum led_status {
- LED_OFF = 0,
- LED_ON = 1,
- LED_FLIP = 2,
- LED_STATUS = 3,
+ LED_OFF = 0,
+ LED_SCANNING = 1,
+ LED_ASSOCIATED = 2,
};
-int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status);
-int zd_chip_led_flip(struct zd_chip *chip, int led,
- const unsigned int *phases_msecs, unsigned int count);
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status);
int zd_set_beacon_interval(struct zd_chip *chip, u32 interval);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 1989f1c05fb..2d12837052b 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -33,6 +33,10 @@
static void ieee_init(struct ieee80211_device *ieee);
static void softmac_init(struct ieee80211softmac_device *sm);
+static void housekeeping_init(struct zd_mac *mac);
+static void housekeeping_enable(struct zd_mac *mac);
+static void housekeeping_disable(struct zd_mac *mac);
+
int zd_mac_init(struct zd_mac *mac,
struct net_device *netdev,
struct usb_interface *intf)
@@ -46,6 +50,7 @@ int zd_mac_init(struct zd_mac *mac,
ieee_init(ieee);
softmac_init(ieee80211_priv(netdev));
zd_chip_init(&mac->chip, netdev, intf);
+ housekeeping_init(mac);
return 0;
}
@@ -178,6 +183,7 @@ int zd_mac_open(struct net_device *netdev)
if (r < 0)
goto disable_rx;
+ housekeeping_enable(mac);
ieee80211softmac_start(netdev);
return 0;
disable_rx:
@@ -204,6 +210,7 @@ int zd_mac_stop(struct net_device *netdev)
*/
zd_chip_disable_rx(chip);
+ housekeeping_disable(mac);
ieee80211softmac_stop(netdev);
zd_chip_disable_hwint(chip);
@@ -1080,3 +1087,46 @@ void zd_dump_rx_status(const struct rx_status *status)
}
}
#endif /* DEBUG */
+
+#define LINK_LED_WORK_DELAY HZ
+
+static void link_led_handler(void *p)
+{
+ struct zd_mac *mac = p;
+ struct zd_chip *chip = &mac->chip;
+ struct ieee80211softmac_device *sm = ieee80211_priv(mac->netdev);
+ int is_associated;
+ int r;
+
+ spin_lock_irq(&mac->lock);
+ is_associated = sm->associated != 0;
+ spin_unlock_irq(&mac->lock);
+
+ r = zd_chip_control_leds(chip,
+ is_associated ? LED_ASSOCIATED : LED_SCANNING);
+ if (r)
+ dev_err(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r);
+
+ queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+ LINK_LED_WORK_DELAY);
+}
+
+static void housekeeping_init(struct zd_mac *mac)
+{
+ INIT_WORK(&mac->housekeeping.link_led_work, link_led_handler, mac);
+}
+
+static void housekeeping_enable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+ 0);
+}
+
+static void housekeeping_disable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ cancel_rearming_delayed_workqueue(zd_workqueue,
+ &mac->housekeeping.link_led_work);
+ zd_chip_control_leds(&mac->chip, LED_OFF);
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index 29b51fd7d4e..b8ea3de7924 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -120,6 +120,10 @@ enum mac_flags {
MAC_FIXED_CHANNEL = 0x01,
};
+struct housekeeping {
+ struct work_struct link_led_work;
+};
+
#define ZD_MAC_STATS_BUFFER_SIZE 16
struct zd_mac {
@@ -128,6 +132,7 @@ struct zd_mac {
struct net_device *netdev;
/* Unlocked reading possible */
struct iw_statistics iw_stats;
+ struct housekeeping housekeeping;
unsigned int stats_count;
u8 qual_buffer[ZD_MAC_STATS_BUFFER_SIZE];
u8 rssi_buffer[ZD_MAC_STATS_BUFFER_SIZE];
diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c
index 440ef24b5fd..af3a7b36d07 100644
--- a/drivers/net/wireless/zd1211rw/zd_netdev.c
+++ b/drivers/net/wireless/zd1211rw/zd_netdev.c
@@ -82,7 +82,7 @@ static int iw_get_nick(struct net_device *netdev,
union iwreq_data *req, char *extra)
{
strcpy(extra, "zd1211");
- req->data.length = strlen(extra) + 1;
+ req->data.length = strlen(extra);
req->data.flags = 1;
return 0;
}
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 31027e52b04..5c265ad0485 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -24,6 +24,7 @@
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
+#include <linux/workqueue.h>
#include <net/ieee80211.h>
#include "zd_def.h"
@@ -1112,12 +1113,20 @@ static struct usb_driver driver = {
.disconnect = disconnect,
};
+struct workqueue_struct *zd_workqueue;
+
static int __init usb_init(void)
{
int r;
pr_debug("usb_init()\n");
+ zd_workqueue = create_singlethread_workqueue(driver.name);
+ if (zd_workqueue == NULL) {
+ printk(KERN_ERR "%s: couldn't create workqueue\n", driver.name);
+ return -ENOMEM;
+ }
+
r = usb_register(&driver);
if (r) {
printk(KERN_ERR "usb_register() failed. Error number %d\n", r);
@@ -1132,6 +1141,7 @@ static void __exit usb_exit(void)
{
pr_debug("usb_exit()\n");
usb_deregister(&driver);
+ destroy_workqueue(zd_workqueue);
}
module_init(usb_init);
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index ded39de5f72..e81a2d3cfff 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -238,4 +238,6 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits);
+extern struct workqueue_struct *zd_workqueue;
+
#endif /* _ZD_USB_H */
diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c
index 71c2da277d6..5756401fb15 100644
--- a/drivers/oprofile/oprofilefs.c
+++ b/drivers/oprofile/oprofilefs.c
@@ -31,7 +31,6 @@ static struct inode * oprofilefs_get_inode(struct super_block * sb, int mode)
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
@@ -110,8 +109,8 @@ static ssize_t ulong_write_file(struct file * file, char const __user * buf, siz
static int default_open(struct inode * inode, struct file * filp)
{
- if (inode->u.generic_ip)
- filp->private_data = inode->u.generic_ip;
+ if (inode->i_private)
+ filp->private_data = inode->i_private;
return 0;
}
@@ -158,7 +157,7 @@ int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root,
if (!d)
return -EFAULT;
- d->d_inode->u.generic_ip = val;
+ d->d_inode->i_private = val;
return 0;
}
@@ -171,7 +170,7 @@ int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root,
if (!d)
return -EFAULT;
- d->d_inode->u.generic_ip = val;
+ d->d_inode->i_private = val;
return 0;
}
@@ -197,7 +196,7 @@ int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root,
if (!d)
return -EFAULT;
- d->d_inode->u.generic_ip = val;
+ d->d_inode->i_private = val;
return 0;
}
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index 98b83a85c60..78c0a269a2b 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -374,6 +374,7 @@ static void __devexit parport_serial_pci_remove (struct pci_dev *dev)
return;
}
+#ifdef CONFIG_PM
static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
{
struct parport_serial_private *priv = pci_get_drvdata(dev);
@@ -407,14 +408,17 @@ static int parport_serial_pci_resume(struct pci_dev *dev)
return 0;
}
+#endif
static struct pci_driver parport_serial_pci_driver = {
.name = "parport_serial",
.id_table = parport_serial_pci_tbl,
.probe = parport_serial_pci_probe,
.remove = __devexit_p(parport_serial_pci_remove),
+#ifdef CONFIG_PM
.suspend = parport_serial_pci_suspend,
.resume = parport_serial_pci_resume,
+#endif
};
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 4d762fc4878..c27e782e6df 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -17,6 +17,31 @@ config PCI_MSI
If you don't know what to do here, say N.
+config PCI_MULTITHREAD_PROBE
+ bool "PCI Multi-threaded probe (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ help
+ Say Y here if you want the PCI core to spawn a new thread for
+ every PCI device that is probed. This can cause a huge
+ speedup in boot times on multiprocessor machines, and even a
+ smaller speedup on single processor machines.
+
+ But it can also cause lots of bad things to happen. A number
+ of PCI drivers can not properly handle running in this way,
+ some will just not work properly at all, while others might
+ decide to blow up power supplies with a huge load all at once,
+ so use this option at your own risk.
+
+ It is very unwise to use this option if you are not using a
+ boot process that can handle devices being created in any
+ order. A program that can create persistant block and network
+ device names (like udev) is a good idea if you wish to use
+ this option.
+
+ Again, use this option at your own risk, you have been warned!
+
+ When in doubt, say N.
+
config PCI_DEBUG
bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 5f7db9d2436..aadaa3c8096 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -77,9 +77,12 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
* This adds a single pci device to the global
* device list and adds sysfs and procfs entries
*/
-void __devinit pci_bus_add_device(struct pci_dev *dev)
+int __devinit pci_bus_add_device(struct pci_dev *dev)
{
- device_add(&dev->dev);
+ int retval;
+ retval = device_add(&dev->dev);
+ if (retval)
+ return retval;
down_write(&pci_bus_sem);
list_add_tail(&dev->global_list, &pci_devices);
@@ -87,6 +90,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
pci_proc_attach_device(dev);
pci_create_sysfs_dev_files(dev);
+ return 0;
}
/**
@@ -104,6 +108,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
void __devinit pci_bus_add_devices(struct pci_bus *bus)
{
struct pci_dev *dev;
+ int retval;
list_for_each_entry(dev, &bus->devices, bus_list) {
/*
@@ -112,7 +117,9 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus)
*/
if (!list_empty(&dev->global_list))
continue;
- pci_bus_add_device(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "Error adding device, continuing\n");
}
list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -129,10 +136,13 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus)
list_add_tail(&dev->subordinate->node,
&dev->bus->children);
up_write(&pci_bus_sem);
- }
+ }
pci_bus_add_devices(dev->subordinate);
-
- sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge");
+ retval = sysfs_create_link(&dev->subordinate->class_dev.kobj,
+ &dev->dev.kobj, "bridge");
+ if (retval)
+ dev_err(&dev->dev, "Error creating sysfs "
+ "bridge symlink, continuing...\n");
}
}
}
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index be104eced34..7fff07e877c 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -150,6 +150,11 @@ struct acpiphp_attention_info
struct module *owner;
};
+struct acpiphp_ioapic {
+ struct pci_dev *dev;
+ u32 gsi_base;
+ struct list_head list;
+};
/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index ae67a8f55ba..83e8e4412de 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,6 +53,8 @@
#include "acpiphp.h"
static LIST_HEAD(bridge_list);
+static LIST_HEAD(ioapic_list);
+static DEFINE_SPINLOCK(ioapic_list_lock);
#define MY_NAME "acpiphp_glue"
@@ -797,6 +799,7 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
struct pci_dev *pdev;
u32 gsi_base;
u64 phys_addr;
+ struct acpiphp_ioapic *ioapic;
/* Evaluate _STA if present */
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
@@ -811,41 +814,107 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
if (get_gsi_base(handle, &gsi_base))
return AE_OK;
+ ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);
+ if (!ioapic)
+ return AE_NO_MEMORY;
+
pdev = get_apic_pci_info(handle);
if (!pdev)
- return AE_OK;
+ goto exit_kfree;
- if (pci_enable_device(pdev)) {
- pci_dev_put(pdev);
- return AE_OK;
- }
+ if (pci_enable_device(pdev))
+ goto exit_pci_dev_put;
pci_set_master(pdev);
- if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)")) {
- pci_disable_device(pdev);
- pci_dev_put(pdev);
- return AE_OK;
- }
+ if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))
+ goto exit_pci_disable_device;
phys_addr = pci_resource_start(pdev, 0);
- if (acpi_register_ioapic(handle, phys_addr, gsi_base)) {
- pci_release_region(pdev, 0);
- pci_disable_device(pdev);
- pci_dev_put(pdev);
+ if (acpi_register_ioapic(handle, phys_addr, gsi_base))
+ goto exit_pci_release_region;
+
+ ioapic->gsi_base = gsi_base;
+ ioapic->dev = pdev;
+ spin_lock(&ioapic_list_lock);
+ list_add_tail(&ioapic->list, &ioapic_list);
+ spin_unlock(&ioapic_list_lock);
+
+ return AE_OK;
+
+ exit_pci_release_region:
+ pci_release_region(pdev, 0);
+ exit_pci_disable_device:
+ pci_disable_device(pdev);
+ exit_pci_dev_put:
+ pci_dev_put(pdev);
+ exit_kfree:
+ kfree(ioapic);
+
+ return AE_OK;
+}
+
+static acpi_status
+ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ acpi_status status;
+ unsigned long sta;
+ acpi_handle tmp;
+ u32 gsi_base;
+ struct acpiphp_ioapic *pos, *n, *ioapic = NULL;
+
+ /* Evaluate _STA if present */
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
+ return AE_CTRL_DEPTH;
+
+ /* Scan only PCI bus scope */
+ status = acpi_get_handle(handle, "_HID", &tmp);
+ if (ACPI_SUCCESS(status))
+ return AE_CTRL_DEPTH;
+
+ if (get_gsi_base(handle, &gsi_base))
return AE_OK;
+
+ acpi_unregister_ioapic(handle, gsi_base);
+
+ spin_lock(&ioapic_list_lock);
+ list_for_each_entry_safe(pos, n, &ioapic_list, list) {
+ if (pos->gsi_base != gsi_base)
+ continue;
+ ioapic = pos;
+ list_del(&ioapic->list);
+ break;
}
+ spin_unlock(&ioapic_list_lock);
+
+ if (!ioapic)
+ return AE_OK;
+
+ pci_release_region(ioapic->dev, 0);
+ pci_disable_device(ioapic->dev);
+ pci_dev_put(ioapic->dev);
+ kfree(ioapic);
return AE_OK;
}
static int acpiphp_configure_ioapics(acpi_handle handle)
{
+ ioapic_add(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, ioapic_add, NULL, NULL);
return 0;
}
+static int acpiphp_unconfigure_ioapics(acpi_handle handle)
+{
+ ioapic_remove(handle, 0, NULL, NULL);
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+ ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
+ return 0;
+}
+
static int power_on_slot(struct acpiphp_slot *slot)
{
acpi_status status;
@@ -997,7 +1066,7 @@ acpiphp_bus_add_out:
* @handle: handle to acpi namespace
*
*/
-int acpiphp_bus_trim(acpi_handle handle)
+static int acpiphp_bus_trim(acpi_handle handle)
{
struct acpi_device *device;
int retval;
@@ -1074,10 +1143,11 @@ static int enable_device(struct acpiphp_slot *slot)
pci_bus_assign_resources(bus);
acpiphp_sanitize_bus(bus);
+ acpiphp_set_hpp_values(slot->bridge->handle, bus);
+ list_for_each_entry(func, &slot->funcs, sibling)
+ acpiphp_configure_ioapics(func->handle);
pci_enable_bridges(bus);
pci_bus_add_devices(bus);
- acpiphp_set_hpp_values(slot->bridge->handle, bus);
- acpiphp_configure_ioapics(slot->bridge->handle);
/* associate pci_dev to our representation */
list_for_each (l, &slot->funcs) {
@@ -1103,6 +1173,16 @@ static int enable_device(struct acpiphp_slot *slot)
return retval;
}
+static void disable_bridges(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ if (dev->subordinate) {
+ disable_bridges(dev->subordinate);
+ pci_disable_device(dev);
+ }
+ }
+}
/**
* disable_device - disable a slot
@@ -1127,6 +1207,19 @@ static int disable_device(struct acpiphp_slot *slot)
func->bridge = NULL;
}
+ if (func->pci_dev) {
+ pci_stop_bus_device(func->pci_dev);
+ if (func->pci_dev->subordinate) {
+ disable_bridges(func->pci_dev->subordinate);
+ pci_disable_device(func->pci_dev);
+ }
+ }
+ }
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ acpiphp_unconfigure_ioapics(func->handle);
acpiphp_bus_trim(func->handle);
/* try to remove anyway.
* acpiphp_bus_add might have been failed */
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index 317457dd401..d0a07d9ab30 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void)
if (ACPI_FAILURE(status))
err("%s: Notification handler removal failed\n", __FUNCTION__);
/* remove the /sys entries */
- if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr))
- err("%s: removal of sysfs file apci_table failed\n",
- __FUNCTION__);
+ sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
}
module_init(ibm_acpiphp_init);
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c
index 8b3da007e85..5bab666cd67 100644
--- a/drivers/pci/hotplug/cpqphp_sysfs.c
+++ b/drivers/pci/hotplug/cpqphp_sysfs.c
@@ -140,7 +140,7 @@ struct ctrl_dbg {
static int open(struct inode *inode, struct file *file)
{
- struct controller *ctrl = inode->u.generic_ip;
+ struct controller *ctrl = inode->i_private;
struct ctrl_dbg *dbg;
int retval = -ENOMEM;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index dd2b762777c..05a4f0f9018 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -176,7 +176,9 @@ static void pci_rescan_slot(struct pci_dev *temp)
struct pci_bus *bus = temp->bus;
struct pci_dev *dev;
int func;
+ int retval;
u8 hdr_type;
+
if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
temp->hdr_type = hdr_type & 0x7f;
if (!pci_find_slot(bus->number, temp->devfn)) {
@@ -185,8 +187,12 @@ static void pci_rescan_slot(struct pci_dev *temp)
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
- pci_bus_add_device(dev);
- add_slot(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "error adding "
+ "device, continuing.\n");
+ else
+ add_slot(dev);
}
}
/* multifunction device? */
@@ -205,8 +211,12 @@ static void pci_rescan_slot(struct pci_dev *temp)
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
- pci_bus_add_device(dev);
- add_slot(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "error adding "
+ "device, continuing.\n");
+ else
+ add_slot(dev);
}
}
}
diff --git a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h
index e929b7c1142..772523dc386 100644
--- a/drivers/pci/hotplug/pci_hotplug.h
+++ b/drivers/pci/hotplug/pci_hotplug.h
@@ -172,8 +172,8 @@ struct hotplug_slot {
extern int pci_hp_register (struct hotplug_slot *slot);
extern int pci_hp_deregister (struct hotplug_slot *slot);
-extern int pci_hp_change_slot_info (struct hotplug_slot *slot,
- struct hotplug_slot_info *info);
+extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot,
+ struct hotplug_slot_info *info);
extern struct subsystem pci_hotplug_slots_subsys;
/* PCI Setting Record (Type 0) */
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index b7b378df89e..e2823ea9c4e 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -482,31 +482,95 @@ static int has_test_file (struct hotplug_slot *slot)
static int fs_add_slot (struct hotplug_slot *slot)
{
- if (has_power_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ int retval = 0;
- if (has_attention_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+ if (has_power_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ if (retval)
+ goto exit_power;
+ }
- if (has_latch_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+ if (has_attention_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
+ if (retval)
+ goto exit_attention;
+ }
- if (has_adapter_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+ if (has_latch_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_latch.attr);
+ if (retval)
+ goto exit_latch;
+ }
- if (has_address_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+ if (has_adapter_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
+ if (retval)
+ goto exit_adapter;
+ }
- if (has_max_bus_speed_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+ if (has_address_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_address.attr);
+ if (retval)
+ goto exit_address;
+ }
+ if (has_max_bus_speed_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_max_bus_speed.attr);
+ if (retval)
+ goto exit_max_speed;
+ }
+
+ if (has_cur_bus_speed_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_cur_bus_speed.attr);
+ if (retval)
+ goto exit_cur_speed;
+ }
+
+ if (has_test_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_test.attr);
+ if (retval)
+ goto exit_test;
+ }
+
+ goto exit;
+
+exit_test:
if (has_cur_bus_speed_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
- if (has_test_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr);
+exit_cur_speed:
+ if (has_max_bus_speed_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
- return 0;
+exit_max_speed:
+ if (has_address_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+
+exit_address:
+ if (has_adapter_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+
+exit_adapter:
+ if (has_latch_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+
+exit_latch:
+ if (has_attention_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+
+exit_attention:
+ if (has_power_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+exit_power:
+exit:
+ return retval;
}
static void fs_remove_slot (struct hotplug_slot *slot)
@@ -626,8 +690,11 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info)
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+ struct hotplug_slot_info *info)
{
+ int retval;
+
if ((slot == NULL) || (info == NULL))
return -ENODEV;
@@ -636,32 +703,60 @@ int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info
* for the files referring to the fields that have now changed.
*/
if ((has_power_file(slot) == 0) &&
- (slot->info->power_status != info->power_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ (slot->info->power_status != info->power_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_power.attr);
+ if (retval)
+ return retval;
+ }
if ((has_attention_file(slot) == 0) &&
- (slot->info->attention_status != info->attention_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+ (slot->info->attention_status != info->attention_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
+ if (retval)
+ return retval;
+ }
if ((has_latch_file(slot) == 0) &&
- (slot->info->latch_status != info->latch_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+ (slot->info->latch_status != info->latch_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_latch.attr);
+ if (retval)
+ return retval;
+ }
if ((has_adapter_file(slot) == 0) &&
- (slot->info->adapter_status != info->adapter_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+ (slot->info->adapter_status != info->adapter_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
+ if (retval)
+ return retval;
+ }
if ((has_address_file(slot) == 0) &&
- (slot->info->address != info->address))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+ (slot->info->address != info->address)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_address.attr);
+ if (retval)
+ return retval;
+ }
if ((has_max_bus_speed_file(slot) == 0) &&
- (slot->info->max_bus_speed != info->max_bus_speed))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+ (slot->info->max_bus_speed != info->max_bus_speed)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_max_bus_speed.attr);
+ if (retval)
+ return retval;
+ }
if ((has_cur_bus_speed_file(slot) == 0) &&
- (slot->info->cur_bus_speed != info->cur_bus_speed))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+ (slot->info->cur_bus_speed != info->cur_bus_speed)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_cur_bus_speed.attr);
+ if (retval)
+ return retval;
+ }
memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 33d19876835..41290a106bd 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -762,14 +762,14 @@ int pciehp_enable_slot(struct slot *p_slot)
if (rc || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (rc || getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
@@ -778,7 +778,7 @@ int pciehp_enable_slot(struct slot *p_slot)
if (rc || getstatus) {
info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -EINVAL;
}
}
mutex_unlock(&p_slot->ctrl->crit_sect);
@@ -813,7 +813,7 @@ int pciehp_disable_slot(struct slot *p_slot)
if (ret || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
@@ -822,7 +822,7 @@ int pciehp_disable_slot(struct slot *p_slot)
if (ret || getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
@@ -831,7 +831,7 @@ int pciehp_disable_slot(struct slot *p_slot)
if (ret || !getstatus) {
info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -EINVAL;
}
}
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c
index 8ad446605f7..2b9e10e3861 100644
--- a/drivers/pci/hotplug/pcihp_skeleton.c
+++ b/drivers/pci/hotplug/pcihp_skeleton.c
@@ -1,5 +1,5 @@
/*
- * PCI Hot Plug Controller Skeleton Driver - 0.2
+ * PCI Hot Plug Controller Skeleton Driver - 0.3
*
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001,2003 IBM Corp.
@@ -21,7 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * This driver is to be used as a skeleton driver to be show how to interface
+ * This driver is to be used as a skeleton driver to show how to interface
* with the pci hotplug core easily.
*
* Send feedback to <greg@kroah.com>
@@ -58,8 +58,6 @@ static LIST_HEAD(slot_list);
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
-
-
/* local variables */
static int debug;
static int num_slots;
@@ -109,7 +107,6 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return retval;
}
-
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
@@ -342,7 +339,7 @@ static int __init pcihp_skel_init(void)
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
/*
* Do specific initialization stuff for your driver here
- * Like initializing your controller hardware (if any) and
+ * like initializing your controller hardware (if any) and
* determining the number of slots you have in the system
* right now.
*/
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index 7208b95c6ee..c7103ac5cd0 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -173,7 +173,7 @@ struct controller {
#define msg_button_cancel "PCI slot #%s - action canceled due to button press.\n"
/* sysfs functions for the hotplug controller info */
-extern void shpchp_create_ctrl_files (struct controller *ctrl);
+extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl);
extern int shpchp_sysfs_enable_slot(struct slot *slot);
extern int shpchp_sysfs_disable_slot(struct slot *slot);
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index a14e7de1984..235c18a2239 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -449,10 +449,14 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ctrl->speed = PCI_SPEED_33MHz;
}
- shpchp_create_ctrl_files(ctrl);
+ rc = shpchp_create_ctrl_files(ctrl);
+ if (rc)
+ goto err_cleanup_slots;
return 0;
+err_cleanup_slots:
+ cleanup_slots(ctrl);
err_out_release_ctlr:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
index 620e1139e60..29fa9d26ada 100644
--- a/drivers/pci/hotplug/shpchp_sysfs.c
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -91,9 +91,9 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
-void shpchp_create_ctrl_files (struct controller *ctrl)
+int __must_check shpchp_create_ctrl_files (struct controller *ctrl)
{
- device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
+ return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
}
void shpchp_remove_ctrl_files(struct controller *ctrl)
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index a83c1f5735d..27a057409ec 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -45,16 +45,10 @@ msi_register(struct msi_ops *ops)
return 0;
}
-static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags)
-{
- memset(p, 0, sizeof(struct msi_desc));
-}
-
static int msi_cache_init(void)
{
- msi_cachep = kmem_cache_create("msi_cache",
- sizeof(struct msi_desc),
- 0, SLAB_HWCACHE_ALIGN, msi_cache_ctor, NULL);
+ msi_cachep = kmem_cache_create("msi_cache", sizeof(struct msi_desc),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!msi_cachep)
return -ENOMEM;
@@ -402,11 +396,10 @@ static struct msi_desc* alloc_msi_entry(void)
{
struct msi_desc *entry;
- entry = kmem_cache_alloc(msi_cachep, SLAB_KERNEL);
+ entry = kmem_cache_zalloc(msi_cachep, GFP_KERNEL);
if (!entry)
return NULL;
- memset(entry, 0, sizeof(struct msi_desc));
entry->link.tail = entry->link.head = 0; /* single message */
entry->dev = NULL;
@@ -901,6 +894,33 @@ static int msix_capability_init(struct pci_dev *dev,
}
/**
+ * pci_msi_supported - check whether MSI may be enabled on device
+ * @dev: pointer to the pci_dev data structure of MSI device function
+ *
+ * MSI must be globally enabled and supported by the device and its root
+ * bus. But, the root bus is not easy to find since some architectures
+ * have virtual busses on top of the PCI hierarchy (for instance the
+ * hypertransport bus), while the actual bus where MSI must be supported
+ * is below. So we test the MSI flag on all parent busses and assume
+ * that no quirk will ever set the NO_MSI flag on a non-root bus.
+ **/
+static
+int pci_msi_supported(struct pci_dev * dev)
+{
+ struct pci_bus *bus;
+
+ if (!pci_msi_enable || !dev || dev->no_msi)
+ return -EINVAL;
+
+ /* check MSI flags of all parent busses */
+ for (bus = dev->bus; bus; bus = bus->parent)
+ if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
* pci_enable_msi - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
*
@@ -912,19 +932,11 @@ static int msix_capability_init(struct pci_dev *dev,
**/
int pci_enable_msi(struct pci_dev* dev)
{
- struct pci_bus *bus;
- int pos, temp, status = -EINVAL;
+ int pos, temp, status;
u16 control;
- if (!pci_msi_enable || !dev)
- return status;
-
- if (dev->no_msi)
- return status;
-
- for (bus = dev->bus; bus; bus = bus->parent)
- if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
- return -EINVAL;
+ if (pci_msi_supported(dev) < 0)
+ return -EINVAL;
temp = dev->irq;
@@ -1134,22 +1146,14 @@ static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec)
**/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
- struct pci_bus *bus;
int status, pos, nr_entries, free_vectors;
int i, j, temp;
u16 control;
unsigned long flags;
- if (!pci_msi_enable || !dev || !entries)
+ if (!entries || pci_msi_supported(dev) < 0)
return -EINVAL;
- if (dev->no_msi)
- return -EINVAL;
-
- for (bus = dev->bus; bus; bus = bus->parent)
- if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
- return -EINVAL;
-
status = msi_init();
if (status < 0)
return status;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 474e9cd0e9e..b1c0c707d96 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -17,6 +17,16 @@
* Registration of PCI drivers and handling of hot-pluggable devices.
*/
+/* multithreaded probe logic */
+static int pci_multithread_probe =
+#ifdef CONFIG_PCI_MULTITHREAD_PROBE
+ 1;
+#else
+ 0;
+#endif
+__module_param_call("", pci_multithread_probe, param_set_bool, param_get_bool, &pci_multithread_probe, 0644);
+
+
/*
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/
@@ -46,6 +56,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
subdevice=PCI_ANY_ID, class=0, class_mask=0;
unsigned long driver_data=0;
int fields=0;
+ int retval = 0;
fields = sscanf(buf, "%x %x %x %x %x %x %lux",
&vendor, &device, &subvendor, &subdevice,
@@ -72,10 +83,12 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
spin_unlock(&pdrv->dynids.lock);
if (get_driver(&pdrv->driver)) {
- driver_attach(&pdrv->driver);
+ retval = driver_attach(&pdrv->driver);
put_driver(&pdrv->driver);
}
+ if (retval)
+ return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -279,6 +292,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
return i;
}
+static int pci_device_suspend_late(struct device * dev, pm_message_t state)
+{
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+ struct pci_driver * drv = pci_dev->driver;
+ int i = 0;
+
+ if (drv && drv->suspend_late) {
+ i = drv->suspend_late(pci_dev, state);
+ suspend_report_result(drv->suspend_late, i);
+ }
+ return i;
+}
/*
* Default resume method for devices that have no driver provided resume,
@@ -313,6 +338,17 @@ static int pci_device_resume(struct device * dev)
return error;
}
+static int pci_device_resume_early(struct device * dev)
+{
+ int error = 0;
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+ struct pci_driver * drv = pci_dev->driver;
+
+ if (drv && drv->resume_early)
+ error = drv->resume_early(pci_dev);
+ return error;
+}
+
static void pci_device_shutdown(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -386,6 +422,11 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner)
drv->driver.owner = owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
+ if (pci_multithread_probe)
+ drv->driver.multithread_probe = pci_multithread_probe;
+ else
+ drv->driver.multithread_probe = drv->multithread_probe;
+
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
@@ -509,8 +550,10 @@ struct bus_type pci_bus_type = {
.probe = pci_device_probe,
.remove = pci_device_remove,
.suspend = pci_device_suspend,
- .shutdown = pci_device_shutdown,
+ .suspend_late = pci_device_suspend_late,
+ .resume_early = pci_device_resume_early,
.resume = pci_device_resume,
+ .shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
};
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index fdefa7dcd15..a1d2e979b17 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -117,6 +117,7 @@ is_enabled_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
+ int retval = 0;
/* this can crash the machine when done on the "wrong" device */
if (!capable(CAP_SYS_ADMIN))
@@ -126,11 +127,53 @@ is_enabled_store(struct device *dev, struct device_attribute *attr,
pci_disable_device(pdev);
if (*buf == '1')
- pci_enable_device(pdev);
+ retval = pci_enable_device(pdev);
+ if (retval)
+ return retval;
return count;
}
+static ssize_t
+msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (!pdev->subordinate)
+ return 0;
+
+ return sprintf (buf, "%u\n",
+ !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
+}
+
+static ssize_t
+msi_bus_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ /* bad things may happen if the no_msi flag is changed
+ * while some drivers are loaded */
+ if (!capable(CAP_SYS_ADMIN))
+ return count;
+
+ if (!pdev->subordinate)
+ return count;
+
+ if (*buf == '0') {
+ pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ dev_warn(&pdev->dev, "forced subordinate bus to not support MSI,"
+ " bad things could happen.\n");
+ }
+
+ if (*buf == '1') {
+ pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
+ dev_warn(&pdev->dev, "forced subordinate bus to support MSI,"
+ " bad things could happen.\n");
+ }
+
+ return count;
+}
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
@@ -145,6 +188,7 @@ struct device_attribute pci_dev_attrs[] = {
__ATTR(enable, 0600, is_enabled_show, is_enabled_store),
__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
broken_parity_status_show,broken_parity_status_store),
+ __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
__ATTR_NULL,
};
@@ -385,15 +429,38 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
}
/**
+ * pci_remove_resource_files - cleanup resource files
+ * @dev: dev to cleanup
+ *
+ * If we created resource files for @dev, remove them from sysfs and
+ * free their resources.
+ */
+static void
+pci_remove_resource_files(struct pci_dev *pdev)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+ struct bin_attribute *res_attr;
+
+ res_attr = pdev->res_attr[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
+ }
+}
+
+/**
* pci_create_resource_files - create resource files in sysfs for @dev
* @dev: dev in question
*
* Walk the resources in @dev creating files for each resource available.
*/
-static void
-pci_create_resource_files(struct pci_dev *pdev)
+static int pci_create_resource_files(struct pci_dev *pdev)
{
int i;
+ int retval;
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
@@ -416,35 +483,19 @@ pci_create_resource_files(struct pci_dev *pdev)
res_attr->size = pci_resource_len(pdev, i);
res_attr->mmap = pci_mmap_resource;
res_attr->private = &pdev->resource[i];
- sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
- }
- }
-}
-
-/**
- * pci_remove_resource_files - cleanup resource files
- * @dev: dev to cleanup
- *
- * If we created resource files for @dev, remove them from sysfs and
- * free their resources.
- */
-static void
-pci_remove_resource_files(struct pci_dev *pdev)
-{
- int i;
-
- for (i = 0; i < PCI_ROM_RESOURCE; i++) {
- struct bin_attribute *res_attr;
-
- res_attr = pdev->res_attr[i];
- if (res_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
- kfree(res_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+ if (retval) {
+ pci_remove_resource_files(pdev);
+ return retval;
+ }
+ } else {
+ return -ENOMEM;
}
}
+ return 0;
}
#else /* !HAVE_PCI_MMAP */
-static inline void pci_create_resource_files(struct pci_dev *dev) { return; }
+static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; }
static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
#endif /* HAVE_PCI_MMAP */
@@ -529,22 +580,27 @@ static struct bin_attribute pcie_config_attr = {
.write = pci_write_config,
};
-int pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
{
+ struct bin_attribute *rom_attr = NULL;
+ int retval;
+
if (!sysfs_initialized)
return -EACCES;
if (pdev->cfg_size < 4096)
- sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
- sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ if (retval)
+ goto err;
- pci_create_resource_files(pdev);
+ retval = pci_create_resource_files(pdev);
+ if (retval)
+ goto err_bin_file;
/* If the device has a ROM, try to expose it in sysfs. */
if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
- struct bin_attribute *rom_attr;
-
rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC);
if (rom_attr) {
pdev->rom_attr = rom_attr;
@@ -554,13 +610,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev)
rom_attr->attr.owner = THIS_MODULE;
rom_attr->read = pci_read_rom;
rom_attr->write = pci_write_rom;
- sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+ if (retval)
+ goto err_rom;
+ } else {
+ retval = -ENOMEM;
+ goto err_bin_file;
}
}
/* add platform-specific attributes */
pcibios_add_platform_entries(pdev);
-
+
return 0;
+
+err_rom:
+ kfree(rom_attr);
+err_bin_file:
+ if (pdev->cfg_size < 4096)
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+ else
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+err:
+ return retval;
}
/**
@@ -589,10 +660,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
static int __init pci_sysfs_init(void)
{
struct pci_dev *pdev = NULL;
-
+ int retval;
+
sysfs_initialized = 1;
- for_each_pci_dev(pdev)
- pci_create_sysfs_dev_files(pdev);
+ for_each_pci_dev(pdev) {
+ retval = pci_create_sysfs_dev_files(pdev);
+ if (retval)
+ return retval;
+ }
return 0;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 9f79dd6d51a..a544997399b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -432,10 +432,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
case PM_EVENT_ON:
return PCI_D0;
case PM_EVENT_FREEZE:
+ case PM_EVENT_PRETHAW:
+ /* REVISIT both freeze and pre-thaw "should" use D0 */
case PM_EVENT_SUSPEND:
return PCI_D3hot;
default:
- printk("They asked me for state %d\n", state.event);
+ printk("Unrecognized suspend event %d\n", state.event);
BUG();
}
return PCI_D0;
@@ -443,6 +445,51 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
EXPORT_SYMBOL(pci_choose_state);
+static int pci_save_pcie_state(struct pci_dev *dev)
+{
+ int pos, i = 0;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (pos <= 0)
+ return 0;
+
+ save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL);
+ if (!save_state) {
+ dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ return -ENOMEM;
+ }
+ cap = (u16 *)&save_state->data[0];
+
+ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
+ pci_add_saved_cap(dev, save_state);
+ return 0;
+}
+
+static void pci_restore_pcie_state(struct pci_dev *dev)
+{
+ int i = 0, pos;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!save_state || pos <= 0)
+ return;
+ cap = (u16 *)&save_state->data[0];
+
+ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
+ pci_remove_saved_cap(save_state);
+ kfree(save_state);
+}
+
/**
* pci_save_state - save the PCI configuration space of a device before suspending
* @dev: - PCI device that we're dealing with
@@ -458,6 +505,8 @@ pci_save_state(struct pci_dev *dev)
return i;
if ((i = pci_save_msix_state(dev)) != 0)
return i;
+ if ((i = pci_save_pcie_state(dev)) != 0)
+ return i;
return 0;
}
@@ -471,6 +520,9 @@ pci_restore_state(struct pci_dev *dev)
int i;
int val;
+ /* PCI Express register must be restored first */
+ pci_restore_pcie_state(dev);
+
/*
* The Base Address register should be programmed before the command
* register(s)
@@ -953,13 +1005,12 @@ static int __devinit pci_setup(char *str)
}
str = k;
}
- return 1;
+ return 0;
}
+early_param("pci", pci_setup);
device_initcall(pci_init);
-__setup("pci=", pci_setup);
-
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
/* FIXME: Some boxes have multiple ISA bridges! */
struct pci_dev *isa_bridge;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 08d58fc78ee..6bf327db5c5 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -42,7 +42,7 @@ extern void pci_remove_legacy_files(struct pci_bus *bus);
/* Lock for read/write access to pci device and bus lists */
extern struct rw_semaphore pci_bus_sem;
-#ifdef CONFIG_X86_IO_APIC
+#ifdef CONFIG_PCI_MSI
extern int pci_msi_quirk;
#else
#define pci_msi_quirk 0
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 1012db8b8b2..0ad92a8ad8b 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -34,3 +34,4 @@ config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
When in doubt, say N.
+source "drivers/pci/pcie/aer/Kconfig"
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 984fa87283e..e00fb99acf4 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -5,3 +5,6 @@
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
+
+# Build PCI Express AER if needed
+obj-$(CONFIG_PCIEAER) += aer/
diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig
new file mode 100644
index 00000000000..3f37a60a643
--- /dev/null
+++ b/drivers/pci/pcie/aer/Kconfig
@@ -0,0 +1,12 @@
+#
+# PCI Express Root Port Device AER Configuration
+#
+
+config PCIEAER
+ boolean "Root Port Advanced Error Reporting support"
+ depends on PCIEPORTBUS && ACPI
+ default y
+ help
+ This enables PCI Express Root Port Advanced Error Reporting
+ (AER) driver support. Error reporting messages sent to Root
+ Port will be handled by PCI Express AER driver.
diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile
new file mode 100644
index 00000000000..15a4f40d520
--- /dev/null
+++ b/drivers/pci/pcie/aer/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for PCI-Express Root Port Advanced Error Reporting Driver
+#
+
+obj-$(CONFIG_PCIEAER) += aerdriver.o
+
+aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o aerdrv_acpi.o
+
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
new file mode 100644
index 00000000000..0d4ac027d53
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -0,0 +1,346 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv.c
+ *
+ * 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.
+ *
+ * This file implements the AER root port service driver. The driver will
+ * register an irq handler. When root port triggers an AER interrupt, the irq
+ * handler will collect root port status and schedule a work.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pcieport_if.h>
+
+#include "aerdrv.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
+#define DRIVER_DESC "Root Port Advanced Error Reporting Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int __devinit aer_probe (struct pcie_device *dev,
+ const struct pcie_port_service_id *id );
+static void aer_remove(struct pcie_device *dev);
+static int aer_suspend(struct pcie_device *dev, pm_message_t state)
+{return 0;}
+static int aer_resume(struct pcie_device *dev) {return 0;}
+static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error);
+static void aer_error_resume(struct pci_dev *dev);
+static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
+
+/*
+ * PCI Express bus's AER Root service driver data structure
+ */
+static struct pcie_port_service_id aer_id[] = {
+ {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .port_type = PCIE_RC_PORT,
+ .service_type = PCIE_PORT_SERVICE_AER,
+ },
+ { /* end: all zeroes */ }
+};
+
+static struct pci_error_handlers aer_error_handlers = {
+ .error_detected = aer_error_detected,
+ .resume = aer_error_resume,
+};
+
+static struct pcie_port_service_driver aerdrv = {
+ .name = "aer",
+ .id_table = &aer_id[0],
+
+ .probe = aer_probe,
+ .remove = aer_remove,
+
+ .suspend = aer_suspend,
+ .resume = aer_resume,
+
+ .err_handler = &aer_error_handlers,
+
+ .reset_link = aer_root_reset,
+};
+
+/**
+ * aer_irq - Root Port's ISR
+ * @irq: IRQ assigned to Root Port
+ * @context: pointer to Root Port data structure
+ * @r: pointer struct pt_regs
+ *
+ * Invoked when Root Port detects AER messages.
+ **/
+static irqreturn_t aer_irq(int irq, void *context, struct pt_regs * r)
+{
+ unsigned int status, id;
+ struct pcie_device *pdev = (struct pcie_device *)context;
+ struct aer_rpc *rpc = get_service_data(pdev);
+ int next_prod_idx;
+ unsigned long flags;
+ int pos;
+
+ pos = pci_find_aer_capability(pdev->port);
+ /*
+ * Must lock access to Root Error Status Reg, Root Error ID Reg,
+ * and Root error producer/consumer index
+ */
+ spin_lock_irqsave(&rpc->e_lock, flags);
+
+ /* Read error status */
+ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
+ if (!(status & ROOT_ERR_STATUS_MASKS)) {
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return IRQ_NONE;
+ }
+
+ /* Read error source and clear error status */
+ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id);
+ pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
+
+ /* Store error source for later DPC handler */
+ next_prod_idx = rpc->prod_idx + 1;
+ if (next_prod_idx == AER_ERROR_SOURCES_MAX)
+ next_prod_idx = 0;
+ if (next_prod_idx == rpc->cons_idx) {
+ /*
+ * Error Storm Condition - possibly the same error occurred.
+ * Drop the error.
+ */
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return IRQ_HANDLED;
+ }
+ rpc->e_sources[rpc->prod_idx].status = status;
+ rpc->e_sources[rpc->prod_idx].id = id;
+ rpc->prod_idx = next_prod_idx;
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+
+ /* Invoke DPC handler */
+ schedule_work(&rpc->dpc_handler);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * aer_alloc_rpc - allocate Root Port data structure
+ * @dev: pointer to the pcie_dev data structure
+ *
+ * Invoked when Root Port's AER service is loaded.
+ **/
+static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev)
+{
+ struct aer_rpc *rpc;
+
+ if (!(rpc = (struct aer_rpc *)kmalloc(sizeof(struct aer_rpc),
+ GFP_KERNEL)))
+ return NULL;
+
+ memset(rpc, 0, sizeof(struct aer_rpc));
+ /*
+ * Initialize Root lock access, e_lock, to Root Error Status Reg,
+ * Root Error ID Reg, and Root error producer/consumer index.
+ */
+ rpc->e_lock = SPIN_LOCK_UNLOCKED;
+
+ rpc->rpd = dev;
+ INIT_WORK(&rpc->dpc_handler, aer_isr, (void *)dev);
+ rpc->prod_idx = rpc->cons_idx = 0;
+ mutex_init(&rpc->rpc_mutex);
+ init_waitqueue_head(&rpc->wait_release);
+
+ /* Use PCIE bus function to store rpc into PCIE device */
+ set_service_data(dev, rpc);
+
+ return rpc;
+}
+
+/**
+ * aer_remove - clean up resources
+ * @dev: pointer to the pcie_dev data structure
+ *
+ * Invoked when PCI Express bus unloads or AER probe fails.
+ **/
+static void aer_remove(struct pcie_device *dev)
+{
+ struct aer_rpc *rpc = get_service_data(dev);
+
+ if (rpc) {
+ /* If register interrupt service, it must be free. */
+ if (rpc->isr)
+ free_irq(dev->irq, dev);
+
+ wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx);
+
+ aer_delete_rootport(rpc);
+ set_service_data(dev, NULL);
+ }
+}
+
+/**
+ * aer_probe - initialize resources
+ * @dev: pointer to the pcie_dev data structure
+ * @id: pointer to the service id data structure
+ *
+ * Invoked when PCI Express bus loads AER service driver.
+ **/
+static int __devinit aer_probe (struct pcie_device *dev,
+ const struct pcie_port_service_id *id )
+{
+ int status;
+ struct aer_rpc *rpc;
+ struct device *device = &dev->device;
+
+ /* Init */
+ if ((status = aer_init(dev)))
+ return status;
+
+ /* Alloc rpc data structure */
+ if (!(rpc = aer_alloc_rpc(dev))) {
+ printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n",
+ __FUNCTION__, device->bus_id);
+ aer_remove(dev);
+ return -ENOMEM;
+ }
+
+ /* Request IRQ ISR */
+ if ((status = request_irq(dev->irq, aer_irq, SA_SHIRQ, "aerdrv",
+ dev))) {
+ printk(KERN_DEBUG "%s: Request ISR fails on PCIE device[%s]\n",
+ __FUNCTION__, device->bus_id);
+ aer_remove(dev);
+ return status;
+ }
+
+ rpc->isr = 1;
+
+ aer_enable_rootport(rpc);
+
+ return status;
+}
+
+/**
+ * aer_root_reset - reset link on Root Port
+ * @dev: pointer to Root Port's pci_dev data structure
+ *
+ * Invoked by Port Bus driver when performing link reset at Root Port.
+ **/
+static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
+{
+ u16 p2p_ctrl;
+ u32 status;
+ int pos;
+
+ pos = pci_find_aer_capability(dev);
+
+ /* Disable Root's interrupt in response to error messages */
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0);
+
+ /* Assert Secondary Bus Reset */
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
+ p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+ /* De-assert Secondary Bus Reset */
+ p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+ /*
+ * System software must wait for at least 100ms from the end
+ * of a reset of one or more device before it is permitted
+ * to issue Configuration Requests to those devices.
+ */
+ msleep(200);
+ printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
+ pci_write_config_dword(dev,
+ pos + PCI_ERR_ROOT_COMMAND,
+ ROOT_PORT_INTR_ON_MESG_MASK);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * aer_error_detected - update severity status
+ * @dev: pointer to Root Port's pci_dev data structure
+ * @error: error severity being notified by port bus
+ *
+ * Invoked by Port Bus driver during error recovery.
+ **/
+static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error)
+{
+ /* Root Port has no impact. Always recovers. */
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+/**
+ * aer_error_resume - clean up corresponding error status bits
+ * @dev: pointer to Root Port's pci_dev data structure
+ *
+ * Invoked by Port Bus driver during nonfatal recovery.
+ **/
+static void aer_error_resume(struct pci_dev *dev)
+{
+ int pos;
+ u32 status, mask;
+ u16 reg16;
+
+ /* Clean up Root device status */
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
+ pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
+
+ /* Clean AER Root Error Status */
+ pos = pci_find_aer_capability(dev);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
+ if (dev->error_state == pci_channel_io_normal)
+ status &= ~mask; /* Clear corresponding nonfatal bits */
+ else
+ status &= mask; /* Clear corresponding fatal bits */
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+}
+
+/**
+ * aer_service_init - register AER root service driver
+ *
+ * Invoked when AER root service driver is loaded.
+ **/
+static int __init aer_service_init(void)
+{
+ return pcie_port_service_register(&aerdrv);
+}
+
+/**
+ * aer_service_exit - unregister AER root service driver
+ *
+ * Invoked when AER root service driver is unloaded.
+ **/
+static void __exit aer_service_exit(void)
+{
+ pcie_port_service_unregister(&aerdrv);
+}
+
+module_init(aer_service_init);
+module_exit(aer_service_exit);
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
new file mode 100644
index 00000000000..daf0cad88fc
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#ifndef _AERDRV_H_
+#define _AERDRV_H_
+
+#include <linux/pcieport_if.h>
+#include <linux/aer.h>
+
+#define AER_NONFATAL 0
+#define AER_FATAL 1
+#define AER_CORRECTABLE 2
+#define AER_UNCORRECTABLE 4
+#define AER_ERROR_MASK 0x001fffff
+#define AER_ERROR(d) (d & AER_ERROR_MASK)
+
+#define OSC_METHOD_RUN_SUCCESS 0
+#define OSC_METHOD_NOT_SUPPORTED 1
+#define OSC_METHOD_RUN_FAILURE 2
+
+/* Root Error Status Register Bits */
+#define ROOT_ERR_STATUS_MASKS 0x0f
+
+#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
+ PCI_EXP_RTCTL_SENFEE| \
+ PCI_EXP_RTCTL_SEFEE)
+#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \
+ PCI_ERR_ROOT_CMD_NONFATAL_EN| \
+ PCI_ERR_ROOT_CMD_FATAL_EN)
+#define ERR_COR_ID(d) (d & 0xffff)
+#define ERR_UNCOR_ID(d) (d >> 16)
+
+#define AER_SUCCESS 0
+#define AER_UNSUCCESS 1
+#define AER_ERROR_SOURCES_MAX 100
+
+#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \
+ PCI_ERR_UNC_ECRC| \
+ PCI_ERR_UNC_UNSUP| \
+ PCI_ERR_UNC_COMP_ABORT| \
+ PCI_ERR_UNC_UNX_COMP| \
+ PCI_ERR_UNC_MALF_TLP)
+
+/* AER Error Info Flags */
+#define AER_TLP_HEADER_VALID_FLAG 0x00000001
+#define AER_MULTI_ERROR_VALID_FLAG 0x00000002
+
+#define ERR_CORRECTABLE_ERROR_MASK 0x000031c1
+#define ERR_UNCORRECTABLE_ERROR_MASK 0x001ff010
+
+struct header_log_regs {
+ unsigned int dw0;
+ unsigned int dw1;
+ unsigned int dw2;
+ unsigned int dw3;
+};
+
+struct aer_err_info {
+ int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */
+ int flags;
+ unsigned int status; /* COR/UNCOR Error Status */
+ struct header_log_regs tlp; /* TLP Header */
+};
+
+struct aer_err_source {
+ unsigned int status;
+ unsigned int id;
+};
+
+struct aer_rpc {
+ struct pcie_device *rpd; /* Root Port device */
+ struct work_struct dpc_handler;
+ struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX];
+ unsigned short prod_idx; /* Error Producer Index */
+ unsigned short cons_idx; /* Error Consumer Index */
+ int isr;
+ spinlock_t e_lock; /*
+ * Lock access to Error Status/ID Regs
+ * and error producer/consumer index
+ */
+ struct mutex rpc_mutex; /*
+ * only one thread could do
+ * recovery on the same
+ * root port hierachy
+ */
+ wait_queue_head_t wait_release;
+};
+
+struct aer_broadcast_data {
+ enum pci_channel_state state;
+ enum pci_ers_result result;
+};
+
+static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
+ enum pci_ers_result new)
+{
+ switch (orig) {
+ case PCI_ERS_RESULT_CAN_RECOVER:
+ case PCI_ERS_RESULT_RECOVERED:
+ orig = new;
+ break;
+ case PCI_ERS_RESULT_DISCONNECT:
+ if (new == PCI_ERS_RESULT_NEED_RESET)
+ orig = new;
+ break;
+ default:
+ break;
+ }
+
+ return orig;
+}
+
+extern struct bus_type pcie_port_bus_type;
+extern void aer_enable_rootport(struct aer_rpc *rpc);
+extern void aer_delete_rootport(struct aer_rpc *rpc);
+extern int aer_init(struct pcie_device *dev);
+extern void aer_isr(void *context);
+extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
+extern int aer_osc_setup(struct pci_dev *dev);
+
+#endif //_AERDRV_H_
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
new file mode 100644
index 00000000000..fa68e89ebec
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -0,0 +1,68 @@
+/*
+ * Access ACPI _OSC method
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/delay.h>
+#include "aerdrv.h"
+
+/**
+ * aer_osc_setup - run ACPI _OSC method
+ *
+ * Return:
+ * Zero if success. Nonzero for otherwise.
+ *
+ * Invoked when PCIE bus loads AER service driver. To avoid conflict with
+ * BIOS AER support requires BIOS to yield AER control to OS native driver.
+ **/
+int aer_osc_setup(struct pci_dev *dev)
+{
+ int retval = OSC_METHOD_RUN_SUCCESS;
+ acpi_status status;
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+ struct pci_dev *pdev = dev;
+ struct pci_bus *parent;
+
+ while (!handle) {
+ if (!pdev || !pdev->bus->parent)
+ break;
+ parent = pdev->bus->parent;
+ if (!parent->self)
+ /* Parent must be a host bridge */
+ handle = acpi_get_pci_rootbridge_handle(
+ pci_domain_nr(parent),
+ parent->number);
+ else
+ handle = DEVICE_ACPI_HANDLE(
+ &(parent->self->dev));
+ pdev = parent->self;
+ }
+
+ if (!handle)
+ return OSC_METHOD_NOT_SUPPORTED;
+
+ pci_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT);
+ status = pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_SUPPORT)
+ retval = OSC_METHOD_NOT_SUPPORTED;
+ else
+ retval = OSC_METHOD_RUN_FAILURE;
+ }
+
+ return retval;
+}
+
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
new file mode 100644
index 00000000000..1c7e660d653
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -0,0 +1,758 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv_core.c
+ *
+ * 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.
+ *
+ * This file implements the core part of PCI-Express AER. When an pci-express
+ * error is delivered, an error message will be collected and printed to
+ * console, then, an error recovery procedure will be executed by following
+ * the pci error recovery rules.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/delay.h>
+#include "aerdrv.h"
+
+static int forceload;
+module_param(forceload, bool, 0);
+
+#define PCI_CFG_SPACE_SIZE (0x100)
+int pci_find_aer_capability(struct pci_dev *dev)
+{
+ int pos;
+ u32 reg32 = 0;
+
+ /* Check if it's a pci-express device */
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return 0;
+
+ /* Check if it supports pci-express AER */
+ pos = PCI_CFG_SPACE_SIZE;
+ while (pos) {
+ if (pci_read_config_dword(dev, pos, &reg32))
+ return 0;
+
+ /* some broken boards return ~0 */
+ if (reg32 == 0xffffffff)
+ return 0;
+
+ if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR)
+ break;
+
+ pos = reg32 >> 20;
+ }
+
+ return pos;
+}
+
+int pci_enable_pcie_error_reporting(struct pci_dev *dev)
+{
+ u16 reg16 = 0;
+ int pos;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 |
+ PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE;
+ pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
+ reg16);
+ return 0;
+}
+
+int pci_disable_pcie_error_reporting(struct pci_dev *dev)
+{
+ u16 reg16 = 0;
+ int pos;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE);
+ pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
+ reg16);
+ return 0;
+}
+
+int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
+{
+ int pos;
+ u32 status, mask;
+
+ pos = pci_find_aer_capability(dev);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
+ if (dev->error_state == pci_channel_io_normal)
+ status &= ~mask; /* Clear corresponding nonfatal bits */
+ else
+ status &= mask; /* Clear corresponding fatal bits */
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+
+ return 0;
+}
+
+static int find_device_iter(struct device *device, void *data)
+{
+ struct pci_dev *dev;
+ u16 id = *(unsigned long *)data;
+ u8 secondary, subordinate, d_bus = id >> 8;
+
+ if (device->bus == &pci_bus_type) {
+ dev = to_pci_dev(device);
+ if (id == ((dev->bus->number << 8) | dev->devfn)) {
+ /*
+ * Device ID match
+ */
+ *(unsigned long*)data = (unsigned long)device;
+ return 1;
+ }
+
+ /*
+ * If device is P2P, check if it is an upstream?
+ */
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(dev, PCI_SECONDARY_BUS,
+ &secondary);
+ pci_read_config_byte(dev, PCI_SUBORDINATE_BUS,
+ &subordinate);
+ if (d_bus >= secondary && d_bus <= subordinate) {
+ *(unsigned long*)data = (unsigned long)device;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * find_source_device - search through device hierarchy for source device
+ * @p_dev: pointer to Root Port pci_dev data structure
+ * @id: device ID of agent who sends an error message to this Root Port
+ *
+ * Invoked when error is detected at the Root Port.
+ **/
+static struct device* find_source_device(struct pci_dev *parent, u16 id)
+{
+ struct pci_dev *dev = parent;
+ struct device *device;
+ unsigned long device_addr;
+ int status;
+
+ /* Is Root Port an agent that sends error message? */
+ if (id == ((dev->bus->number << 8) | dev->devfn))
+ return &dev->dev;
+
+ do {
+ device_addr = id;
+ if ((status = device_for_each_child(&dev->dev,
+ &device_addr, find_device_iter))) {
+ device = (struct device*)device_addr;
+ dev = to_pci_dev(device);
+ if (id == ((dev->bus->number << 8) | dev->devfn))
+ return device;
+ }
+ }while (status);
+
+ return NULL;
+}
+
+static void report_error_detected(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ dev->error_state = result_data->state;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->error_detected) {
+ if (result_data->state == pci_channel_io_frozen &&
+ !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) {
+ /*
+ * In case of fatal recovery, if one of down-
+ * stream device has no driver. We might be
+ * unable to recover because a later insmod
+ * of a driver for this device is unaware of
+ * its hw state.
+ */
+ printk(KERN_DEBUG "Device ID[%s] has %s\n",
+ dev->dev.bus_id, (dev->driver) ?
+ "no AER-aware driver" : "no driver");
+ }
+ return;
+ }
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->error_detected(dev, result_data->state);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_mmio_enabled(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->mmio_enabled)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->mmio_enabled(dev);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_slot_reset(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->slot_reset)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->slot_reset(dev);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_resume(struct pci_dev *dev, void *data)
+{
+ struct pci_error_handlers *err_handler;
+
+ dev->error_state = pci_channel_io_normal;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->slot_reset)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ err_handler->resume(dev);
+ return;
+}
+
+/**
+ * broadcast_error_message - handle message broadcast to downstream drivers
+ * @device: pointer to from where in a hierarchy message is broadcasted down
+ * @api: callback to be broadcasted
+ * @state: error state
+ *
+ * Invoked during error recovery process. Once being invoked, the content
+ * of error severity will be broadcasted to all downstream drivers in a
+ * hierarchy in question.
+ **/
+static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
+ enum pci_channel_state state,
+ char *error_mesg,
+ void (*cb)(struct pci_dev *, void *))
+{
+ struct aer_broadcast_data result_data;
+
+ printk(KERN_DEBUG "Broadcast %s message\n", error_mesg);
+ result_data.state = state;
+ if (cb == report_error_detected)
+ result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
+ else
+ result_data.result = PCI_ERS_RESULT_RECOVERED;
+
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+ /*
+ * If the error is reported by a bridge, we think this error
+ * is related to the downstream link of the bridge, so we
+ * do error recovery on all subordinates of the bridge instead
+ * of the bridge and clear the error status of the bridge.
+ */
+ if (cb == report_error_detected)
+ dev->error_state = state;
+ pci_walk_bus(dev->subordinate, cb, &result_data);
+ if (cb == report_resume) {
+ pci_cleanup_aer_uncorrect_error_status(dev);
+ dev->error_state = pci_channel_io_normal;
+ }
+ }
+ else {
+ /*
+ * If the error is reported by an end point, we think this
+ * error is related to the upstream link of the end point.
+ */
+ pci_walk_bus(dev->bus, cb, &result_data);
+ }
+
+ return result_data.result;
+}
+
+struct find_aer_service_data {
+ struct pcie_port_service_driver *aer_driver;
+ int is_downstream;
+};
+
+static int find_aer_service_iter(struct device *device, void *data)
+{
+ struct device_driver *driver;
+ struct pcie_port_service_driver *service_driver;
+ struct pcie_device *pcie_dev;
+ struct find_aer_service_data *result;
+
+ result = (struct find_aer_service_data *) data;
+
+ if (device->bus == &pcie_port_bus_type) {
+ pcie_dev = to_pcie_device(device);
+ if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT)
+ result->is_downstream = 1;
+
+ driver = device->driver;
+ if (driver) {
+ service_driver = to_service_driver(driver);
+ if (service_driver->id_table->service_type ==
+ PCIE_PORT_SERVICE_AER) {
+ result->aer_driver = service_driver;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void find_aer_service(struct pci_dev *dev,
+ struct find_aer_service_data *data)
+{
+ int retval;
+ retval = device_for_each_child(&dev->dev, data, find_aer_service_iter);
+}
+
+static pci_ers_result_t reset_link(struct pcie_device *aerdev,
+ struct pci_dev *dev)
+{
+ struct pci_dev *udev;
+ pci_ers_result_t status;
+ struct find_aer_service_data data;
+
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
+ udev = dev;
+ else
+ udev= dev->bus->self;
+
+ data.is_downstream = 0;
+ data.aer_driver = NULL;
+ find_aer_service(udev, &data);
+
+ /*
+ * Use the aer driver of the error agent firstly.
+ * If it hasn't the aer driver, use the root port's
+ */
+ if (!data.aer_driver || !data.aer_driver->reset_link) {
+ if (data.is_downstream &&
+ aerdev->device.driver &&
+ to_service_driver(aerdev->device.driver)->reset_link) {
+ data.aer_driver =
+ to_service_driver(aerdev->device.driver);
+ } else {
+ printk(KERN_DEBUG "No link-reset support to Device ID"
+ "[%s]\n",
+ dev->dev.bus_id);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ }
+
+ status = data.aer_driver->reset_link(udev);
+ if (status != PCI_ERS_RESULT_RECOVERED) {
+ printk(KERN_DEBUG "Link reset at upstream Device ID"
+ "[%s] failed\n",
+ udev->dev.bus_id);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ return status;
+}
+
+/**
+ * do_recovery - handle nonfatal/fatal error recovery process
+ * @aerdev: pointer to a pcie_device data structure of root port
+ * @dev: pointer to a pci_dev data structure of agent detecting an error
+ * @severity: error severity type
+ *
+ * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
+ * error detected message to all downstream drivers within a hierarchy in
+ * question and return the returned code.
+ **/
+static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
+ struct pci_dev *dev,
+ int severity)
+{
+ pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
+ enum pci_channel_state state;
+
+ if (severity == AER_FATAL)
+ state = pci_channel_io_frozen;
+ else
+ state = pci_channel_io_normal;
+
+ status = broadcast_error_message(dev,
+ state,
+ "error_detected",
+ report_error_detected);
+
+ if (severity == AER_FATAL) {
+ result = reset_link(aerdev, dev);
+ if (result != PCI_ERS_RESULT_RECOVERED) {
+ /* TODO: Should panic here? */
+ return result;
+ }
+ }
+
+ if (status == PCI_ERS_RESULT_CAN_RECOVER)
+ status = broadcast_error_message(dev,
+ state,
+ "mmio_enabled",
+ report_mmio_enabled);
+
+ if (status == PCI_ERS_RESULT_NEED_RESET) {
+ /*
+ * TODO: Should call platform-specific
+ * functions to reset slot before calling
+ * drivers' slot_reset callbacks?
+ */
+ status = broadcast_error_message(dev,
+ state,
+ "slot_reset",
+ report_slot_reset);
+ }
+
+ if (status == PCI_ERS_RESULT_RECOVERED)
+ broadcast_error_message(dev,
+ state,
+ "resume",
+ report_resume);
+
+ return status;
+}
+
+/**
+ * handle_error_source - handle logging error into an event log
+ * @aerdev: pointer to pcie_device data structure of the root port
+ * @dev: pointer to pci_dev data structure of error source device
+ * @info: comprehensive error information
+ *
+ * Invoked when an error being detected by Root Port.
+ **/
+static void handle_error_source(struct pcie_device * aerdev,
+ struct pci_dev *dev,
+ struct aer_err_info info)
+{
+ pci_ers_result_t status = 0;
+ int pos;
+
+ if (info.severity == AER_CORRECTABLE) {
+ /*
+ * Correctable error does not need software intevention.
+ * No need to go through error recovery process.
+ */
+ pos = pci_find_aer_capability(dev);
+ if (pos)
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
+ info.status);
+ } else {
+ status = do_recovery(aerdev, dev, info.severity);
+ if (status == PCI_ERS_RESULT_RECOVERED) {
+ printk(KERN_DEBUG "AER driver successfully recovered\n");
+ } else {
+ /* TODO: Should kernel panic here? */
+ printk(KERN_DEBUG "AER driver didn't recover\n");
+ }
+ }
+}
+
+/**
+ * aer_enable_rootport - enable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus loads AER service driver.
+ **/
+void aer_enable_rootport(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ int pos, aer_pos;
+ u16 reg16;
+ u32 reg32;
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ /* Clear PCIE Capability's Device Status */
+ pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
+ pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
+
+ /* Disable system error generation in response to error messages */
+ pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
+ reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
+ pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
+
+ aer_pos = pci_find_aer_capability(pdev);
+ /* Clear error status */
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
+
+ /* Enable Root Port device reporting error itself */
+ pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 |
+ PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE;
+ pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL,
+ reg16);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_write_config_dword(pdev,
+ aer_pos + PCI_ERR_ROOT_COMMAND,
+ ROOT_PORT_INTR_ON_MESG_MASK);
+}
+
+/**
+ * disable_root_aer - disable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus unloads AER service driver.
+ **/
+static void disable_root_aer(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ u32 reg32;
+ int pos;
+
+ pos = pci_find_aer_capability(pdev);
+ /* Disable Root's interrupt in response to error messages */
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);
+
+ /* Clear Root's error status reg */
+ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
+}
+
+/**
+ * get_e_source - retrieve an error source
+ * @rpc: pointer to the root port which holds an error
+ *
+ * Invoked by DPC handler to consume an error.
+ **/
+static struct aer_err_source* get_e_source(struct aer_rpc *rpc)
+{
+ struct aer_err_source *e_source;
+ unsigned long flags;
+
+ /* Lock access to Root error producer/consumer index */
+ spin_lock_irqsave(&rpc->e_lock, flags);
+ if (rpc->prod_idx == rpc->cons_idx) {
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return NULL;
+ }
+ e_source = &rpc->e_sources[rpc->cons_idx];
+ rpc->cons_idx++;
+ if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
+ rpc->cons_idx = 0;
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+
+ return e_source;
+}
+
+static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
+{
+ int pos;
+
+ pos = pci_find_aer_capability(dev);
+
+ /* The device might not support AER */
+ if (!pos)
+ return AER_SUCCESS;
+
+ if (info->severity == AER_CORRECTABLE) {
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
+ &info->status);
+ if (!(info->status & ERR_CORRECTABLE_ERROR_MASK))
+ return AER_UNSUCCESS;
+ } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
+ info->severity == AER_NONFATAL) {
+
+ /* Link is still healthy for IO reads */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
+ &info->status);
+ if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK))
+ return AER_UNSUCCESS;
+
+ if (info->status & AER_LOG_TLP_MASKS) {
+ info->flags |= AER_TLP_HEADER_VALID_FLAG;
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
+ }
+ }
+
+ return AER_SUCCESS;
+}
+
+/**
+ * aer_isr_one_error - consume an error detected by root port
+ * @p_device: pointer to error root port service device
+ * @e_src: pointer to an error source
+ **/
+static void aer_isr_one_error(struct pcie_device *p_device,
+ struct aer_err_source *e_src)
+{
+ struct device *s_device;
+ struct aer_err_info e_info = {0, 0, 0,};
+ int i;
+ u16 id;
+
+ /*
+ * There is a possibility that both correctable error and
+ * uncorrectable error being logged. Report correctable error first.
+ */
+ for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) {
+ if (i > 4)
+ break;
+ if (!(e_src->status & i))
+ continue;
+
+ /* Init comprehensive error information */
+ if (i & PCI_ERR_ROOT_COR_RCV) {
+ id = ERR_COR_ID(e_src->id);
+ e_info.severity = AER_CORRECTABLE;
+ } else {
+ id = ERR_UNCOR_ID(e_src->id);
+ e_info.severity = ((e_src->status >> 6) & 1);
+ }
+ if (e_src->status &
+ (PCI_ERR_ROOT_MULTI_COR_RCV |
+ PCI_ERR_ROOT_MULTI_UNCOR_RCV))
+ e_info.flags |= AER_MULTI_ERROR_VALID_FLAG;
+ if (!(s_device = find_source_device(p_device->port, id))) {
+ printk(KERN_DEBUG "%s->can't find device of ID%04x\n",
+ __FUNCTION__, id);
+ continue;
+ }
+ if (get_device_error_info(to_pci_dev(s_device), &e_info) ==
+ AER_SUCCESS) {
+ aer_print_error(to_pci_dev(s_device), &e_info);
+ handle_error_source(p_device,
+ to_pci_dev(s_device),
+ e_info);
+ }
+ }
+}
+
+/**
+ * aer_isr - consume errors detected by root port
+ * @context: pointer to a private data of pcie device
+ *
+ * Invoked, as DPC, when root port records new detected error
+ **/
+void aer_isr(void *context)
+{
+ struct pcie_device *p_device = (struct pcie_device *) context;
+ struct aer_rpc *rpc = get_service_data(p_device);
+ struct aer_err_source *e_src;
+
+ mutex_lock(&rpc->rpc_mutex);
+ e_src = get_e_source(rpc);
+ while (e_src) {
+ aer_isr_one_error(p_device, e_src);
+ e_src = get_e_source(rpc);
+ }
+ mutex_unlock(&rpc->rpc_mutex);
+
+ wake_up(&rpc->wait_release);
+}
+
+/**
+ * aer_delete_rootport - disable root port aer and delete service data
+ * @rpc: pointer to a root port device being deleted
+ *
+ * Invoked when AER service unloaded on a specific Root Port
+ **/
+void aer_delete_rootport(struct aer_rpc *rpc)
+{
+ /* Disable root port AER itself */
+ disable_root_aer(rpc);
+
+ kfree(rpc);
+}
+
+/**
+ * aer_init - provide AER initialization
+ * @dev: pointer to AER pcie device
+ *
+ * Invoked when AER service driver is loaded.
+ **/
+int aer_init(struct pcie_device *dev)
+{
+ int status;
+
+ /* Run _OSC Method */
+ status = aer_osc_setup(dev->port);
+
+ if(status != OSC_METHOD_RUN_SUCCESS) {
+ printk(KERN_DEBUG "%s: AER service init fails - %s\n",
+ __FUNCTION__,
+ (status == OSC_METHOD_NOT_SUPPORTED) ?
+ "No ACPI _OSC support" : "Run ACPI _OSC fails");
+
+ if (!forceload)
+ return status;
+ }
+
+ return AER_SUCCESS;
+}
+
+EXPORT_SYMBOL_GPL(pci_find_aer_capability);
+EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
+EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
+EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
+
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c
new file mode 100644
index 00000000000..3933d4f30e8
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -0,0 +1,248 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv_errprint.c
+ *
+ * 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.
+ *
+ * Format error messages and print them to console.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+
+#include "aerdrv.h"
+
+#define AER_AGENT_RECEIVER 0
+#define AER_AGENT_REQUESTER 1
+#define AER_AGENT_COMPLETER 2
+#define AER_AGENT_TRANSMITTER 3
+
+#define AER_AGENT_REQUESTER_MASK (PCI_ERR_UNC_COMP_TIME| \
+ PCI_ERR_UNC_UNSUP)
+
+#define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT
+
+#define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \
+ ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER: 0)))
+
+#define AER_GET_AGENT(t, e) \
+ ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \
+ (e & AER_AGENT_REQUESTER_MASK) ? AER_AGENT_REQUESTER : \
+ (AER_AGENT_TRANSMITTER_MASK(t, e)) ? AER_AGENT_TRANSMITTER : \
+ AER_AGENT_RECEIVER)
+
+#define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR
+#define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \
+ (PCI_ERR_UNC_DLP| \
+ PCI_ERR_COR_BAD_TLP| \
+ PCI_ERR_COR_BAD_DLLP| \
+ PCI_ERR_COR_REP_ROLL| \
+ ((t == AER_CORRECTABLE) ? \
+ PCI_ERR_COR_REP_TIMER: 0))
+
+#define AER_PHYSICAL_LAYER_ERROR 0
+#define AER_DATA_LINK_LAYER_ERROR 1
+#define AER_TRANSACTION_LAYER_ERROR 2
+
+#define AER_GET_LAYER_ERROR(t, e) \
+ ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \
+ AER_PHYSICAL_LAYER_ERROR : \
+ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \
+ AER_DATA_LINK_LAYER_ERROR : \
+ AER_TRANSACTION_LAYER_ERROR)
+
+/*
+ * AER error strings
+ */
+static char* aer_error_severity_string[] = {
+ "Uncorrected (Non-Fatal)",
+ "Uncorrected (Fatal)",
+ "Corrected"
+};
+
+static char* aer_error_layer[] = {
+ "Physical Layer",
+ "Data Link Layer",
+ "Transaction Layer"
+};
+static char* aer_correctable_error_string[] = {
+ "Receiver Error ", /* Bit Position 0 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Bad TLP ", /* Bit Position 6 */
+ "Bad DLLP ", /* Bit Position 7 */
+ "RELAY_NUM Rollover ", /* Bit Position 8 */
+ NULL,
+ NULL,
+ NULL,
+ "Replay Timer Timeout ", /* Bit Position 12 */
+ "Advisory Non-Fatal ", /* Bit Position 13 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static char* aer_uncorrectable_error_string[] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Data Link Protocol ", /* Bit Position 4 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Poisoned TLP ", /* Bit Position 12 */
+ "Flow Control Protocol ", /* Bit Position 13 */
+ "Completion Timeout ", /* Bit Position 14 */
+ "Completer Abort ", /* Bit Position 15 */
+ "Unexpected Completion ", /* Bit Position 16 */
+ "Receiver Overflow ", /* Bit Position 17 */
+ "Malformed TLP ", /* Bit Position 18 */
+ "ECRC ", /* Bit Position 19 */
+ "Unsupported Request ", /* Bit Position 20 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static char* aer_agent_string[] = {
+ "Receiver ID",
+ "Requester ID",
+ "Completer ID",
+ "Transmitter ID"
+};
+
+static char * aer_get_error_source_name(int severity,
+ unsigned int status,
+ char errmsg_buff[])
+{
+ int i;
+ char * errmsg = NULL;
+
+ for (i = 0; i < 32; i++) {
+ if (!(status & (1 << i)))
+ continue;
+
+ if (severity == AER_CORRECTABLE)
+ errmsg = aer_correctable_error_string[i];
+ else
+ errmsg = aer_uncorrectable_error_string[i];
+
+ if (!errmsg) {
+ sprintf(errmsg_buff, "Unknown Error Bit %2d ", i);
+ errmsg = errmsg_buff;
+ }
+
+ break;
+ }
+
+ return errmsg;
+}
+
+static DEFINE_SPINLOCK(logbuf_lock);
+static char errmsg_buff[100];
+void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
+{
+ char * errmsg;
+ int err_layer, agent;
+ char * loglevel;
+
+ if (info->severity == AER_CORRECTABLE)
+ loglevel = KERN_WARNING;
+ else
+ loglevel = KERN_ERR;
+
+ printk("%s+------ PCI-Express Device Error ------+\n", loglevel);
+ printk("%sError Severity\t\t: %s\n", loglevel,
+ aer_error_severity_string[info->severity]);
+
+ if ( info->status == 0) {
+ printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel);
+ printk("%sUnaccessible Received\t: %s\n", loglevel,
+ info->flags & AER_MULTI_ERROR_VALID_FLAG ?
+ "Multiple" : "First");
+ printk("%sUnregistered Agent ID\t: %04x\n", loglevel,
+ (dev->bus->number << 8) | dev->devfn);
+ } else {
+ err_layer = AER_GET_LAYER_ERROR(info->severity, info->status);
+ printk("%sPCIE Bus Error type\t: %s\n", loglevel,
+ aer_error_layer[err_layer]);
+
+ spin_lock(&logbuf_lock);
+ errmsg = aer_get_error_source_name(info->severity,
+ info->status,
+ errmsg_buff);
+ printk("%s%s\t: %s\n", loglevel, errmsg,
+ info->flags & AER_MULTI_ERROR_VALID_FLAG ?
+ "Multiple" : "First");
+ spin_unlock(&logbuf_lock);
+
+ agent = AER_GET_AGENT(info->severity, info->status);
+ printk("%s%s\t\t: %04x\n", loglevel,
+ aer_agent_string[agent],
+ (dev->bus->number << 8) | dev->devfn);
+
+ printk("%sVendorID=%04xh, DeviceID=%04xh,"
+ " Bus=%02xh, Device=%02xh, Function=%02xh\n",
+ loglevel,
+ dev->vendor,
+ dev->device,
+ dev->bus->number,
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+
+ if (info->flags & AER_TLP_HEADER_VALID_FLAG) {
+ unsigned char *tlp = (unsigned char *) &info->tlp;
+ printk("%sTLB Header:\n", loglevel);
+ printk("%s%02x%02x%02x%02x %02x%02x%02x%02x"
+ " %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ loglevel,
+ *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
+ *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4),
+ *(tlp + 11), *(tlp + 10), *(tlp + 9),
+ *(tlp + 8), *(tlp + 15), *(tlp + 14),
+ *(tlp + 13), *(tlp + 12));
+ }
+ }
+}
+
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 1d317d22ee8..67fcd176bab 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -39,7 +39,7 @@ extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state);
extern int pcie_port_device_resume(struct pci_dev *dev);
#endif
extern void pcie_port_device_remove(struct pci_dev *dev);
-extern void pcie_port_bus_register(void);
+extern int pcie_port_bus_register(void);
extern void pcie_port_bus_unregister(void);
#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 3e84b501e6a..3f0976868ed 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -24,6 +24,7 @@ struct bus_type pcie_port_bus_type = {
.suspend = pcie_port_bus_suspend,
.resume = pcie_port_bus_resume,
};
+EXPORT_SYMBOL_GPL(pcie_port_bus_type);
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
{
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 55c66226786..bd6615b4d40 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -6,6 +6,7 @@
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
+#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -339,8 +340,7 @@ static int suspend_iter(struct device *dev, void *data)
int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state)
{
- device_for_each_child(&dev->dev, &state, suspend_iter);
- return 0;
+ return device_for_each_child(&dev->dev, &state, suspend_iter);
}
static int resume_iter(struct device *dev, void *data)
@@ -358,8 +358,7 @@ static int resume_iter(struct device *dev, void *data)
int pcie_port_device_resume(struct pci_dev *dev)
{
- device_for_each_child(&dev->dev, NULL, resume_iter);
- return 0;
+ return device_for_each_child(&dev->dev, NULL, resume_iter);
}
#endif
@@ -402,9 +401,9 @@ void pcie_port_device_remove(struct pci_dev *dev)
pci_disable_msi(dev);
}
-void pcie_port_bus_register(void)
+int __must_check pcie_port_bus_register(void)
{
- bus_register(&pcie_port_bus_type);
+ return bus_register(&pcie_port_bus_type);
}
void pcie_port_bus_unregister(void)
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 478d0d28f7a..037690e08f5 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -14,8 +14,10 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pcieport_if.h>
+#include <linux/aer.h>
#include "portdrv.h"
+#include "aer/aerdrv.h"
/*
* Version Information
@@ -30,6 +32,43 @@ MODULE_LICENSE("GPL");
/* global data */
static const char device_name[] = "pcieport-driver";
+static int pcie_portdrv_save_config(struct pci_dev *dev)
+{
+ return pci_save_state(dev);
+}
+
+#ifdef CONFIG_PM
+static int pcie_portdrv_restore_config(struct pci_dev *dev)
+{
+ int retval;
+
+ pci_restore_state(dev);
+ retval = pci_enable_device(dev);
+ if (retval)
+ return retval;
+ pci_set_master(dev);
+ return 0;
+}
+
+static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ int ret = pcie_port_device_suspend(dev, state);
+
+ if (!ret)
+ ret = pcie_portdrv_save_config(dev);
+ return ret;
+}
+
+static int pcie_portdrv_resume(struct pci_dev *dev)
+{
+ pcie_portdrv_restore_config(dev);
+ return pcie_port_device_resume(dev);
+}
+#else
+#define pcie_portdrv_suspend NULL
+#define pcie_portdrv_resume NULL
+#endif
+
/*
* pcie_portdrv_probe - Probe PCI-Express port devices
* @dev: PCI-Express port device being probed
@@ -61,6 +100,10 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
return -ENOMEM;
}
+ pcie_portdrv_save_config(dev);
+
+ pci_enable_pcie_error_reporting(dev);
+
return 0;
}
@@ -70,39 +113,151 @@ static void pcie_portdrv_remove (struct pci_dev *dev)
kfree(pci_get_drvdata(dev));
}
-#ifdef CONFIG_PM
-static int pcie_portdrv_save_config(struct pci_dev *dev)
+static int error_detected_iter(struct device *device, void *data)
{
- return pci_save_state(dev);
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ struct aer_broadcast_data *result_data;
+ pci_ers_result_t status;
+
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (!driver ||
+ !driver->err_handler ||
+ !driver->err_handler->error_detected)
+ return 0;
+
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error detected message to service drivers */
+ status = driver->err_handler->error_detected(
+ pcie_device->port,
+ result_data->state);
+ result_data->result =
+ merge_result(result_data->result, status);
+ }
+
+ return 0;
}
-static int pcie_portdrv_restore_config(struct pci_dev *dev)
+static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error)
{
+ struct aer_broadcast_data result_data =
+ {error, PCI_ERS_RESULT_CAN_RECOVER};
int retval;
- pci_restore_state(dev);
- retval = pci_enable_device(dev);
- if (retval)
- return retval;
- pci_set_master(dev);
+ /* can not fail */
+ retval = device_for_each_child(&dev->dev, &result_data, error_detected_iter);
+
+ return result_data.result;
+}
+
+static int mmio_enabled_iter(struct device *device, void *data)
+{
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ pci_ers_result_t status, *result;
+
+ result = (pci_ers_result_t *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->mmio_enabled) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ status = driver->err_handler->mmio_enabled(
+ pcie_device->port);
+ *result = merge_result(*result, status);
+ }
+ }
+
return 0;
}
-static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state)
+static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
{
- int ret = pcie_port_device_suspend(dev, state);
+ pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
+ int retval;
- if (!ret)
- ret = pcie_portdrv_save_config(dev);
- return ret;
+ /* get true return value from &status */
+ retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
+ return status;
}
-static int pcie_portdrv_resume (struct pci_dev *dev)
+static int slot_reset_iter(struct device *device, void *data)
{
- pcie_portdrv_restore_config(dev);
- return pcie_port_device_resume(dev);
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ pci_ers_result_t status, *result;
+
+ result = (pci_ers_result_t *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->slot_reset) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ status = driver->err_handler->slot_reset(
+ pcie_device->port);
+ *result = merge_result(*result, status);
+ }
+ }
+
+ return 0;
+}
+
+static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
+{
+ pci_ers_result_t status;
+ int retval;
+
+ /* If fatal, restore cfg space for possible link reset at upstream */
+ if (dev->error_state == pci_channel_io_frozen) {
+ pcie_portdrv_restore_config(dev);
+ pci_enable_pcie_error_reporting(dev);
+ }
+
+ /* get true return value from &status */
+ retval = device_for_each_child(&dev->dev, &status, slot_reset_iter);
+
+ return status;
+}
+
+static int resume_iter(struct device *device, void *data)
+{
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->resume) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ driver->err_handler->resume(pcie_device->port);
+ }
+ }
+
+ return 0;
+}
+
+static void pcie_portdrv_err_resume(struct pci_dev *dev)
+{
+ int retval;
+ /* nothing to do with error value, if it ever happens */
+ retval = device_for_each_child(&dev->dev, NULL, resume_iter);
}
-#endif
/*
* LINUX Device Driver Model
@@ -114,6 +269,13 @@ static const struct pci_device_id port_pci_ids[] = { {
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);
+static struct pci_error_handlers pcie_portdrv_err_handler = {
+ .error_detected = pcie_portdrv_error_detected,
+ .mmio_enabled = pcie_portdrv_mmio_enabled,
+ .slot_reset = pcie_portdrv_slot_reset,
+ .resume = pcie_portdrv_err_resume,
+};
+
static struct pci_driver pcie_portdrv = {
.name = (char *)device_name,
.id_table = &port_pci_ids[0],
@@ -121,20 +283,25 @@ static struct pci_driver pcie_portdrv = {
.probe = pcie_portdrv_probe,
.remove = pcie_portdrv_remove,
-#ifdef CONFIG_PM
.suspend = pcie_portdrv_suspend,
.resume = pcie_portdrv_resume,
-#endif /* PM */
+
+ .err_handler = &pcie_portdrv_err_handler,
};
static int __init pcie_portdrv_init(void)
{
- int retval = 0;
+ int retval;
- pcie_port_bus_register();
+ retval = pcie_port_bus_register();
+ if (retval) {
+ printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
+ goto out;
+ }
retval = pci_register_driver(&pcie_portdrv);
if (retval)
pcie_port_bus_unregister();
+ out:
return retval;
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c5a58d1c6c1..a3b0a5eb505 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -339,6 +339,7 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
{
struct pci_bus *child;
int i;
+ int retval;
/*
* Allocate a new bus, and inherit stuff from the parent..
@@ -356,8 +357,13 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
child->class_dev.class = &pcibus_class;
sprintf(child->class_dev.class_id, "%04x:%02x", pci_domain_nr(child), busnr);
- class_device_register(&child->class_dev);
- class_device_create_file(&child->class_dev, &class_device_attr_cpuaffinity);
+ retval = class_device_register(&child->class_dev);
+ if (retval)
+ goto error_register;
+ retval = class_device_create_file(&child->class_dev,
+ &class_device_attr_cpuaffinity);
+ if (retval)
+ goto error_file_create;
/*
* Set up the primary, secondary and subordinate
@@ -375,6 +381,12 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
bridge->subordinate = child;
return child;
+
+error_file_create:
+ class_device_unregister(&child->class_dev);
+error_register:
+ kfree(child);
+ return NULL;
}
struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index def78a2a7c1..08cd86a6dd6 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -577,8 +577,6 @@ static void __init quirk_ioapic_rmw(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw );
-int pci_msi_quirk;
-
#define AMD8131_revA0 0x01
#define AMD8131_revB0 0x11
#define AMD8131_MISC 0x40
@@ -587,12 +585,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev)
{
unsigned char revid, tmp;
- if (dev->subordinate) {
- printk(KERN_WARNING "PCI: MSI quirk detected. "
- "PCI_BUS_FLAGS_NO_MSI set for subordinate bus.\n");
- dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
- }
-
if (nr_ioapics == 0)
return;
@@ -605,13 +597,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
-
-static void __init quirk_svw_msi(struct pci_dev *dev)
-{
- pci_msi_quirk = 1;
- printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi );
#endif /* CONFIG_X86_IO_APIC */
@@ -1690,6 +1675,95 @@ static void __devinit quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_pcie_aer_ext_cap);
+#ifdef CONFIG_PCI_MSI
+/* To disable MSI globally */
+int pci_msi_quirk;
+
+/* The Serverworks PCI-X chipset does not support MSI. We cannot easily rely
+ * on setting PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
+ * some other busses controlled by the chipset even if Linux is not aware of it.
+ * Instead of setting the flag on all busses in the machine, simply disable MSI
+ * globally.
+ */
+static void __init quirk_svw_msi(struct pci_dev *dev)
+{
+ pci_msi_quirk = 1;
+ printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi);
+
+/* Disable MSI on chipsets that are known to not support it */
+static void __devinit quirk_disable_msi(struct pci_dev *dev)
+{
+ if (dev->subordinate) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "PCI_BUS_FLAGS_NO_MSI set for %s subordinate bus.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_msi);
+
+/* Go through the list of Hypertransport capabilities and
+ * return 1 if a HT MSI capability is found and enabled */
+static int __devinit msi_ht_cap_enabled(struct pci_dev *dev)
+{
+ u8 pos;
+ int ttl;
+ for (pos = pci_find_capability(dev, PCI_CAP_ID_HT), ttl = 48;
+ pos && ttl;
+ pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT), ttl--) {
+ u32 cap_hdr;
+ /* MSI mapping section according to Hypertransport spec */
+ if (pci_read_config_dword(dev, pos, &cap_hdr) == 0
+ && (cap_hdr & 0xf8000000) == 0xa8000000 /* MSI mapping */) {
+ printk(KERN_INFO "PCI: Found HT MSI mapping on %s with capability %s\n",
+ pci_name(dev), cap_hdr & 0x10000 ? "enabled" : "disabled");
+ return (cap_hdr & 0x10000) != 0; /* MSI mapping cap enabled */
+ }
+ }
+ return 0;
+}
+
+/* Check the hypertransport MSI mapping to know whether MSI is enabled or not */
+static void __devinit quirk_msi_ht_cap(struct pci_dev *dev)
+{
+ if (dev->subordinate && !msi_ht_cap_enabled(dev)) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "MSI disabled on chipset %s.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE,
+ quirk_msi_ht_cap);
+
+/* The nVidia CK804 chipset may have 2 HT MSI mappings.
+ * MSI are supported if the MSI capability set in any of these mappings.
+ */
+static void __devinit quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
+{
+ struct pci_dev *pdev;
+
+ if (!dev->subordinate)
+ return;
+
+ /* check HT MSI cap on this chipset and the root one.
+ * a single one having MSI is enough to be sure that MSI are supported.
+ */
+ pdev = pci_find_slot(dev->bus->number, 0);
+ if (dev->subordinate && !msi_ht_cap_enabled(dev)
+ && !msi_ht_cap_enabled(pdev)) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "MSI disabled on chipset %s.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
+ quirk_nvidia_ck804_msi_ht_cap);
+#endif /* CONFIG_PCI_MSI */
+
EXPORT_SYMBOL(pcie_mch_quirk);
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pci_fixup_device);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 99ffbd478b2..430281b2e92 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -16,8 +16,11 @@ static void pci_free_resources(struct pci_dev *dev)
}
}
-static void pci_destroy_dev(struct pci_dev *dev)
+static void pci_stop_dev(struct pci_dev *dev)
{
+ if (!dev->global_list.next)
+ return;
+
if (!list_empty(&dev->global_list)) {
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
@@ -27,6 +30,11 @@ static void pci_destroy_dev(struct pci_dev *dev)
dev->global_list.next = dev->global_list.prev = NULL;
up_write(&pci_bus_sem);
}
+}
+
+static void pci_destroy_dev(struct pci_dev *dev)
+{
+ pci_stop_dev(dev);
/* Remove the device from the device lists, and prevent any further
* list accesses from this device */
@@ -119,5 +127,32 @@ void pci_remove_behind_bridge(struct pci_dev *dev)
}
}
+static void pci_stop_bus_devices(struct pci_bus *bus)
+{
+ struct list_head *l, *n;
+
+ list_for_each_safe(l, n, &bus->devices) {
+ struct pci_dev *dev = pci_dev_b(l);
+ pci_stop_bus_device(dev);
+ }
+}
+
+/**
+ * pci_stop_bus_device - stop a PCI device and any children
+ * @dev: the device to stop
+ *
+ * Stop a PCI device (detach the driver, remove from the global list
+ * and so on). This also stop any subordinate buses and children in a
+ * depth-first manner.
+ */
+void pci_stop_bus_device(struct pci_dev *dev)
+{
+ if (dev->subordinate)
+ pci_stop_bus_devices(dev->subordinate);
+
+ pci_stop_dev(dev);
+}
+
EXPORT_SYMBOL(pci_remove_bus_device);
EXPORT_SYMBOL(pci_remove_behind_bridge);
+EXPORT_SYMBOL_GPL(pci_stop_bus_device);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 47c1071ad84..54404917be9 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -55,12 +55,19 @@ pbus_assign_resources_sorted(struct pci_bus *bus)
list_for_each_entry(dev, &bus->devices, bus_list) {
u16 class = dev->class >> 8;
- /* Don't touch classless devices or host bridges or ioapics. */
+ /* Don't touch classless devices or host bridges. */
if (class == PCI_CLASS_NOT_DEFINED ||
- class == PCI_CLASS_BRIDGE_HOST ||
- class == PCI_CLASS_SYSTEM_PIC)
+ class == PCI_CLASS_BRIDGE_HOST)
continue;
+ /* Don't touch ioapics if it has the assigned resources. */
+ if (class == PCI_CLASS_SYSTEM_PIC) {
+ res = &dev->resource[0];
+ if (res[0].start || res[1].start || res[2].start ||
+ res[3].start || res[4].start || res[5].start)
+ continue;
+ }
+
pdev_sort_resources(dev, &head);
}
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 7ff1d88094b..62c804af9fb 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -27,7 +27,7 @@ config RTC_HCTOSYS
help
If you say yes here, the system time will be set using
the value read from the specified RTC device. This is useful
- in order to avoid unnecessary fschk runs.
+ in order to avoid unnecessary fsck runs.
config RTC_HCTOSYS_DEVICE
string "The RTC to read the time from"
@@ -238,6 +238,16 @@ config RTC_DRV_SA1100
To compile this driver as a module, choose M here: the
module will be called rtc-sa1100.
+config RTC_DRV_SH
+ tristate "SuperH On-Chip RTC"
+ depends on RTC_CLASS && SUPERH
+ help
+ Say Y here to enable support for the on-chip RTC found in
+ most SuperH processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-sh.
+
config RTC_DRV_VR41XX
tristate "NEC VR41XX"
depends on RTC_CLASS && CPU_VR41XX
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index bbcfb09d81d..e72d467ab21 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -31,3 +31,4 @@ obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_AT91) += rtc-at91.o
+obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index 0964d1dba92..25589061f93 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -23,7 +23,7 @@
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
-#define DRV_VERSION "0.1"
+#define DRV_VERSION "0.2"
#define RS5C348_REG_SECS 0
#define RS5C348_REG_MINS 1
@@ -175,8 +175,15 @@ static int __devinit rs5c348_probe(struct spi_device *spi)
goto kfree_exit;
if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) {
u8 buf[2];
+ struct rtc_time tm;
if (ret & RS5C348_BIT_VDET)
dev_warn(&spi->dev, "voltage-low detected.\n");
+ if (ret & RS5C348_BIT_XSTP)
+ dev_warn(&spi->dev, "oscillator-stop detected.\n");
+ rtc_time_to_tm(0, &tm); /* 1970/1/1 */
+ ret = rs5c348_rtc_set_time(&spi->dev, &tm);
+ if (ret < 0)
+ goto kfree_exit;
buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
buf[1] = 0;
ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
new file mode 100644
index 00000000000..d2ce0c8bb8f
--- /dev/null
+++ b/drivers/rtc/rtc-sh.c
@@ -0,0 +1,467 @@
+/*
+ * SuperH On-Chip RTC Support
+ *
+ * Copyright (C) 2006 Paul Mundt
+ *
+ * Based on the old arch/sh/kernel/cpu/rtc.c by:
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_CPU_SH3
+#define rtc_reg_size sizeof(u16)
+#define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */
+#elif defined(CONFIG_CPU_SH4)
+#define rtc_reg_size sizeof(u32)
+#define RTC_BIT_INVERTED 0x40 /* bug on SH7750, SH7750S */
+#endif
+
+#define RTC_REG(r) ((r) * rtc_reg_size)
+
+#define R64CNT RTC_REG(0)
+#define RSECCNT RTC_REG(1)
+#define RMINCNT RTC_REG(2)
+#define RHRCNT RTC_REG(3)
+#define RWKCNT RTC_REG(4)
+#define RDAYCNT RTC_REG(5)
+#define RMONCNT RTC_REG(6)
+#define RYRCNT RTC_REG(7)
+#define RSECAR RTC_REG(8)
+#define RMINAR RTC_REG(9)
+#define RHRAR RTC_REG(10)
+#define RWKAR RTC_REG(11)
+#define RDAYAR RTC_REG(12)
+#define RMONAR RTC_REG(13)
+#define RCR1 RTC_REG(14)
+#define RCR2 RTC_REG(15)
+
+/* RCR1 Bits */
+#define RCR1_CF 0x80 /* Carry Flag */
+#define RCR1_CIE 0x10 /* Carry Interrupt Enable */
+#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
+#define RCR1_AF 0x01 /* Alarm Flag */
+
+/* RCR2 Bits */
+#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
+#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
+#define RCR2_RTCEN 0x08 /* ENable RTC */
+#define RCR2_ADJ 0x04 /* ADJustment (30-second) */
+#define RCR2_RESET 0x02 /* Reset bit */
+#define RCR2_START 0x01 /* Start bit */
+
+struct sh_rtc {
+ void __iomem *regbase;
+ unsigned long regsize;
+ struct resource *res;
+ unsigned int alarm_irq, periodic_irq, carry_irq;
+ struct rtc_device *rtc_dev;
+ spinlock_t lock;
+};
+
+static irqreturn_t sh_rtc_interrupt(int irq, void *id, struct pt_regs *regs)
+{
+ struct platform_device *pdev = id;
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int tmp, events = 0;
+
+ spin_lock(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ if (tmp & RCR1_AF)
+ events |= RTC_AF | RTC_IRQF;
+
+ tmp &= ~(RCR1_CF | RCR1_AF);
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
+
+ spin_unlock(&rtc->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sh_rtc_periodic(int irq, void *id, struct pt_regs *regs)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(id);
+
+ spin_lock(&rtc->lock);
+
+ rtc_update_irq(&rtc->rtc_dev->class_dev, 1, RTC_PF | RTC_IRQF);
+
+ spin_unlock(&rtc->lock);
+
+ return IRQ_HANDLED;
+}
+
+static inline void sh_rtc_setpie(struct device *dev, unsigned int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR2);
+
+ if (enable) {
+ tmp &= ~RCR2_PESMASK;
+ tmp |= RCR2_PEF | (2 << 4);
+ } else
+ tmp &= ~(RCR2_PESMASK | RCR2_PEF);
+
+ writeb(tmp, rtc->regbase + RCR2);
+
+ spin_unlock_irq(&rtc->lock);
+}
+
+static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ if (enable)
+ tmp |= RCR1_AIE;
+ else
+ tmp &= ~RCR1_AIE;
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ spin_unlock_irq(&rtc->lock);
+}
+
+static int sh_rtc_open(struct device *dev)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+ int ret;
+
+ tmp = readb(rtc->regbase + RCR1);
+ tmp &= ~RCR1_CF;
+ tmp |= RCR1_CIE;
+ writeb(tmp, rtc->regbase + RCR1);
+
+ ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, SA_INTERRUPT,
+ "sh-rtc period", dev);
+ if (unlikely(ret)) {
+ dev_err(dev, "request period IRQ failed with %d, IRQ %d\n",
+ ret, rtc->periodic_irq);
+ return ret;
+ }
+
+ ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, SA_INTERRUPT,
+ "sh-rtc carry", dev);
+ if (unlikely(ret)) {
+ dev_err(dev, "request carry IRQ failed with %d, IRQ %d\n",
+ ret, rtc->carry_irq);
+ free_irq(rtc->periodic_irq, dev);
+ goto err_bad_carry;
+ }
+
+ ret = request_irq(rtc->alarm_irq, sh_rtc_interrupt, SA_INTERRUPT,
+ "sh-rtc alarm", dev);
+ if (unlikely(ret)) {
+ dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n",
+ ret, rtc->alarm_irq);
+ goto err_bad_alarm;
+ }
+
+ return 0;
+
+err_bad_alarm:
+ free_irq(rtc->carry_irq, dev);
+err_bad_carry:
+ free_irq(rtc->periodic_irq, dev);
+
+ return ret;
+}
+
+static void sh_rtc_release(struct device *dev)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+
+ sh_rtc_setpie(dev, 0);
+
+ free_irq(rtc->periodic_irq, dev);
+ free_irq(rtc->carry_irq, dev);
+ free_irq(rtc->alarm_irq, dev);
+}
+
+static int sh_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ tmp = readb(rtc->regbase + RCR1);
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ (tmp & RCR1_AIE) ? "yes" : "no");
+ seq_printf(seq, "carry_IRQ\t: %s\n",
+ (tmp & RCR1_CIE) ? "yes" : "no");
+
+ tmp = readb(rtc->regbase + RCR2);
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (tmp & RCR2_PEF) ? "yes" : "no");
+
+ return 0;
+}
+
+static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ unsigned int ret = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ case RTC_PIE_OFF:
+ case RTC_PIE_ON:
+ sh_rtc_setpie(dev, cmd == RTC_PIE_ON);
+ ret = 0;
+ break;
+ case RTC_AIE_OFF:
+ case RTC_AIE_ON:
+ sh_rtc_setaie(dev, cmd == RTC_AIE_ON);
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int sec128, sec2, yr, yr100, cf_bit;
+
+ do {
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+ tmp &= ~RCR1_CF; /* Clear CF-bit */
+ tmp |= RCR1_CIE;
+ writeb(tmp, rtc->regbase + RCR1);
+
+ sec128 = readb(rtc->regbase + R64CNT);
+
+ tm->tm_sec = BCD2BIN(readb(rtc->regbase + RSECCNT));
+ tm->tm_min = BCD2BIN(readb(rtc->regbase + RMINCNT));
+ tm->tm_hour = BCD2BIN(readb(rtc->regbase + RHRCNT));
+ tm->tm_wday = BCD2BIN(readb(rtc->regbase + RWKCNT));
+ tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT));
+ tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT));
+
+#if defined(CONFIG_CPU_SH4)
+ yr = readw(rtc->regbase + RYRCNT);
+ yr100 = BCD2BIN(yr >> 8);
+ yr &= 0xff;
+#else
+ yr = readb(rtc->regbase + RYRCNT);
+ yr100 = BCD2BIN((yr == 0x99) ? 0x19 : 0x20);
+#endif
+
+ tm->tm_year = (yr100 * 100 + BCD2BIN(yr)) - 1900;
+
+ sec2 = readb(rtc->regbase + R64CNT);
+ cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF;
+
+ spin_unlock_irq(&rtc->lock);
+ } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
+
+#if RTC_BIT_INVERTED != 0
+ if ((sec128 & RTC_BIT_INVERTED))
+ tm->tm_sec--;
+#endif
+
+ dev_dbg(&dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __FUNCTION__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(dev, "invalid date\n");
+
+ return 0;
+}
+
+static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int tmp;
+ int year;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* Reset pre-scaler & stop RTC */
+ tmp = readb(rtc->regbase + RCR2);
+ tmp |= RCR2_RESET;
+ writeb(tmp, rtc->regbase + RCR2);
+
+ writeb(BIN2BCD(tm->tm_sec), rtc->regbase + RSECCNT);
+ writeb(BIN2BCD(tm->tm_min), rtc->regbase + RMINCNT);
+ writeb(BIN2BCD(tm->tm_hour), rtc->regbase + RHRCNT);
+ writeb(BIN2BCD(tm->tm_wday), rtc->regbase + RWKCNT);
+ writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT);
+ writeb(BIN2BCD(tm->tm_mon), rtc->regbase + RMONCNT);
+
+#ifdef CONFIG_CPU_SH3
+ year = tm->tm_year % 100;
+ writeb(BIN2BCD(year), rtc->regbase + RYRCNT);
+#else
+ year = (BIN2BCD((tm->tm_year + 1900) / 100) << 8) |
+ BIN2BCD(tm->tm_year % 100);
+ writew(year, rtc->regbase + RYRCNT);
+#endif
+
+ /* Start RTC */
+ tmp = readb(rtc->regbase + RCR2);
+ tmp &= ~RCR2_RESET;
+ tmp |= RCR2_RTCEN | RCR2_START;
+ writeb(tmp, rtc->regbase + RCR2);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static struct rtc_class_ops sh_rtc_ops = {
+ .open = sh_rtc_open,
+ .release = sh_rtc_release,
+ .ioctl = sh_rtc_ioctl,
+ .read_time = sh_rtc_read_time,
+ .set_time = sh_rtc_set_time,
+ .proc = sh_rtc_proc,
+};
+
+static int __devinit sh_rtc_probe(struct platform_device *pdev)
+{
+ struct sh_rtc *rtc;
+ struct resource *res;
+ int ret = -ENOENT;
+
+ rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL);
+ if (unlikely(!rtc))
+ return -ENOMEM;
+
+ spin_lock_init(&rtc->lock);
+
+ rtc->periodic_irq = platform_get_irq(pdev, 0);
+ if (unlikely(rtc->periodic_irq < 0)) {
+ dev_err(&pdev->dev, "No IRQ for period\n");
+ goto err_badres;
+ }
+
+ rtc->carry_irq = platform_get_irq(pdev, 1);
+ if (unlikely(rtc->carry_irq < 0)) {
+ dev_err(&pdev->dev, "No IRQ for carry\n");
+ goto err_badres;
+ }
+
+ rtc->alarm_irq = platform_get_irq(pdev, 2);
+ if (unlikely(rtc->alarm_irq < 0)) {
+ dev_err(&pdev->dev, "No IRQ for alarm\n");
+ goto err_badres;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (unlikely(res == NULL)) {
+ dev_err(&pdev->dev, "No IO resource\n");
+ goto err_badres;
+ }
+
+ rtc->regsize = res->end - res->start + 1;
+
+ rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name);
+ if (unlikely(!rtc->res)) {
+ ret = -EBUSY;
+ goto err_badres;
+ }
+
+ rtc->regbase = (void __iomem *)rtc->res->start;
+ if (unlikely(!rtc->regbase)) {
+ ret = -EINVAL;
+ goto err_badmap;
+ }
+
+ rtc->rtc_dev = rtc_device_register("sh", &pdev->dev,
+ &sh_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc->rtc_dev);
+ goto err_badmap;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+
+err_badmap:
+ release_resource(rtc->res);
+err_badres:
+ kfree(rtc);
+
+ return ret;
+}
+
+static int __devexit sh_rtc_remove(struct platform_device *pdev)
+{
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (likely(rtc->rtc_dev))
+ rtc_device_unregister(rtc->rtc_dev);
+
+ sh_rtc_setpie(&pdev->dev, 0);
+ sh_rtc_setaie(&pdev->dev, 0);
+
+ release_resource(rtc->res);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(rtc);
+
+ return 0;
+}
+static struct platform_driver sh_rtc_platform_driver = {
+ .driver = {
+ .name = "sh-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = sh_rtc_probe,
+ .remove = __devexit_p(sh_rtc_remove),
+};
+
+static int __init sh_rtc_init(void)
+{
+ return platform_driver_register(&sh_rtc_platform_driver);
+}
+
+static void __exit sh_rtc_exit(void)
+{
+ platform_driver_unregister(&sh_rtc_platform_driver);
+}
+
+module_init(sh_rtc_init);
+module_exit(sh_rtc_exit);
+
+MODULE_DESCRIPTION("SuperH on-chip RTC driver");
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 23fa0b28917..9d051e5687e 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -63,44 +63,26 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */
* and function code cmd.
* In case of an exception return 3. Otherwise return result of bitwise OR of
* resulting condition code and DIAG return code. */
-static __inline__ int
-dia250(void *iob, int cmd)
+static inline int dia250(void *iob, int cmd)
{
+ register unsigned long reg0 asm ("0") = (unsigned long) iob;
typedef union {
struct dasd_diag_init_io init_io;
struct dasd_diag_rw_io rw_io;
} addr_type;
int rc;
- __asm__ __volatile__(
-#ifdef CONFIG_64BIT
- " lghi %0,3\n"
- " lgr 0,%3\n"
- " diag 0,%2,0x250\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- " or %0,1\n"
- "1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous\n"
-#else
- " lhi %0,3\n"
- " lr 0,%3\n"
+ rc = 3;
+ asm volatile(
" diag 0,%2,0x250\n"
"0: ipm %0\n"
" srl %0,28\n"
" or %0,1\n"
"1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous\n"
-#endif
- : "=&d" (rc), "=m" (*(addr_type *) iob)
- : "d" (cmd), "d" (iob), "m" (*(addr_type *) iob)
- : "0", "1", "cc");
+ EX_TABLE(0b,1b)
+ : "+d" (rc), "=m" (*(addr_type *) iob)
+ : "d" (cmd), "d" (reg0), "m" (*(addr_type *) iob)
+ : "1", "cc");
return rc;
}
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index cab2c736683..a04d9120cef 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -89,28 +89,15 @@ MODULE_LICENSE("GPL");
*/
static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
{
- int cc;
+ int cc = 2; /* return unused cc 2 if pgin traps */
- __asm__ __volatile__ (
- " lhi %0,2\n" /* return unused cc 2 if pgin traps */
- " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifndef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (cc)
- : "a" (__pa(page_addr)), "a" (xpage_index)
- : "cc" );
+ EX_TABLE(0b,1b)
+ : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
if (cc == 2) {
@@ -137,28 +124,15 @@ static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
*/
static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index)
{
- int cc;
+ int cc = 2; /* return unused cc 2 if pgin traps */
- __asm__ __volatile__ (
- " lhi %0,2\n" /* return unused cc 2 if pgout traps */
- " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifndef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (cc)
- : "a" (__pa(page_addr)), "a" (xpage_index)
- : "cc" );
+ EX_TABLE(0b,1b)
+ : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
if (cc == 2) {
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index ef004d08971..b4557fa3085 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -17,7 +17,6 @@
#include <asm/ccwdev.h>
#include <asm/cio.h>
-#include <asm/cpcmd.h>
#include <asm/ebcdic.h>
#include <asm/idals.h>
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 985d1613baa..31e335751d6 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -100,13 +100,12 @@ service_call(sclp_cmdw_t command, void *sccb)
{
int cc;
- __asm__ __volatile__(
- " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
- " ipm %0\n"
- " srl %0,28"
- : "=&d" (cc)
- : "d" (command), "a" (__pa(sccb))
- : "cc", "memory" );
+ asm volatile(
+ " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
+ " ipm %0\n"
+ " srl %0,28"
+ : "=&d" (cc) : "d" (command), "a" (__pa(sccb))
+ : "cc", "memory");
if (cc == 3)
return -EIO;
if (cc == 2)
@@ -360,16 +359,6 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
sclp_process_queue();
}
-/* Return current Time-Of-Day clock. */
-static inline u64
-sclp_get_clock(void)
-{
- u64 result;
-
- asm volatile ("STCK 0(%1)" : "=m" (result) : "a" (&(result)) : "cc");
- return result;
-}
-
/* Convert interval in jiffies to TOD ticks. */
static inline u64
sclp_tod_from_jiffies(unsigned long jiffies)
@@ -382,7 +371,6 @@ sclp_tod_from_jiffies(unsigned long jiffies)
void
sclp_sync_wait(void)
{
- unsigned long psw_mask;
unsigned long flags;
unsigned long cr0, cr0_sync;
u64 timeout;
@@ -392,7 +380,7 @@ sclp_sync_wait(void)
timeout = 0;
if (timer_pending(&sclp_request_timer)) {
/* Get timeout TOD value */
- timeout = sclp_get_clock() +
+ timeout = get_clock() +
sclp_tod_from_jiffies(sclp_request_timer.expires -
jiffies);
}
@@ -406,13 +394,12 @@ sclp_sync_wait(void)
cr0_sync |= 0x00000200;
cr0_sync &= 0xFFFFF3AC;
__ctl_load(cr0_sync, 0, 0);
- asm volatile ("STOSM 0(%1),0x01"
- : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
+ __raw_local_irq_stosm(0x01);
/* Loop until driver state indicates finished request */
while (sclp_running_state != sclp_running_state_idle) {
/* Check for expired request timer */
if (timer_pending(&sclp_request_timer) &&
- sclp_get_clock() > timeout &&
+ get_clock() > timeout &&
del_timer(&sclp_request_timer))
sclp_request_timer.function(sclp_request_timer.data);
barrier();
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 29718042c6c..06e2eeec847 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -698,7 +698,6 @@ tty3270_alloc_view(void)
if (!tp->freemem_pages)
goto out_tp;
INIT_LIST_HEAD(&tp->freemem);
- init_timer(&tp->timer);
for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
tp->freemem_pages[pages] = (void *)
__get_free_pages(GFP_KERNEL|GFP_DMA, 0);
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index 807320a41fa..4b868f72fe8 100644
--- a/drivers/s390/char/vmwatchdog.c
+++ b/drivers/s390/char/vmwatchdog.c
@@ -54,48 +54,20 @@ enum vmwdt_func {
static int __diag288(enum vmwdt_func func, unsigned int timeout,
char *cmd, size_t len)
{
- register unsigned long __func asm("2");
- register unsigned long __timeout asm("3");
- register unsigned long __cmdp asm("4");
- register unsigned long __cmdl asm("5");
+ register unsigned long __func asm("2") = func;
+ register unsigned long __timeout asm("3") = timeout;
+ register unsigned long __cmdp asm("4") = virt_to_phys(cmd);
+ register unsigned long __cmdl asm("5") = len;
int err;
- __func = func;
- __timeout = timeout;
- __cmdp = virt_to_phys(cmd);
- __cmdl = len;
- err = 0;
- asm volatile (
-#ifdef CONFIG_64BIT
- "diag %2,%4,0x288\n"
- "1: \n"
- ".section .fixup,\"ax\"\n"
- "2: lghi %0,%1\n"
- " jg 1b\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 1b,2b\n"
- ".previous\n"
-#else
- "diag %2,%4,0x288\n"
- "1: \n"
- ".section .fixup,\"ax\"\n"
- "2: lhi %0,%1\n"
- " bras 1,3f\n"
- " .long 1b\n"
- "3: l 1,0(1)\n"
- " br 1\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 1b,2b\n"
- ".previous\n"
-#endif
- : "+&d"(err)
- : "i"(-EINVAL), "d"(__func), "d"(__timeout),
- "d"(__cmdp), "d"(__cmdl)
- : "1", "cc");
+ err = -EINVAL;
+ asm volatile(
+ " diag %1,%3,0x288\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (err) : "d"(__func), "d"(__timeout),
+ "d"(__cmdp), "d"(__cmdl), "0" (-EINVAL) : "1", "cc");
return err;
}
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index 438db483035..1398367b5f6 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -42,18 +42,15 @@ diag210(struct diag210 * addr)
spin_lock_irqsave(&diag210_lock, flags);
diag210_tmp = *addr;
- asm volatile (
- " lhi %0,-1\n"
- " sam31\n"
- " diag %1,0,0x210\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1: sam64\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
- : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory" );
+ asm volatile(
+ " lhi %0,-1\n"
+ " sam31\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1: sam64\n"
+ EX_TABLE(0b,1b)
+ : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory");
*addr = diag210_tmp;
spin_unlock_irqrestore(&diag210_lock, flags);
@@ -66,17 +63,14 @@ diag210(struct diag210 * addr)
{
int ccode;
- asm volatile (
- " lhi %0,-1\n"
- " diag %1,0,0x210\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " lhi %0,-1\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
- : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory" );
+ EX_TABLE(0b,1b)
+ : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory");
return ccode;
}
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 95a9462f9a9..ad6d8294006 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -25,106 +25,74 @@ struct tpi_info {
static inline int stsch(struct subchannel_id schid,
volatile struct schib *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " stsch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " stsch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int stsch_err(struct subchannel_id schid,
volatile struct schib *addr)
{
- int ccode;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
- __asm__ __volatile__(
- " lhi %0,%3\n"
- " lr 1,%1\n"
- " stsch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " stsch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifdef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (ccode)
- : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr)
- : "cc", "1" );
+ EX_TABLE(0b,1b)
+ : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int msch(struct subchannel_id schid,
volatile struct schib *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " msch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " msch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int msch_err(struct subchannel_id schid,
volatile struct schib *addr)
{
- int ccode;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
- __asm__ __volatile__(
- " lhi %0,%3\n"
- " lr 1,%1\n"
- " msch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " msch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifdef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (ccode)
- : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr)
- : "cc", "1" );
+ EX_TABLE(0b,1b)
+ : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int tsch(struct subchannel_id schid,
volatile struct irb *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " tsch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " tsch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
@@ -132,89 +100,77 @@ static inline int tpi( volatile struct tpi_info *addr)
{
int ccode;
- __asm__ __volatile__(
- " tpi 0(%1)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " tpi 0(%1)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int ssch(struct subchannel_id schid,
volatile struct orb *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " ssch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " ssch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int rsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " rsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " rsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int csch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " csch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " csch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int hsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " hsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " hsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int xsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " .insn rre,0xb2760000,%1,0\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " .insn rre,0xb2760000,%1,0\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
@@ -223,41 +179,27 @@ static inline int chsc(void *chsc_area)
typedef struct { char _[4096]; } addr_type;
int cc;
- __asm__ __volatile__ (
- ".insn rre,0xb25f0000,%2,0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " .insn rre,0xb25f0000,%2,0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc), "=m" (*(addr_type *) chsc_area)
: "d" (chsc_area), "m" (*(addr_type *) chsc_area)
- : "cc" );
-
+ : "cc");
return cc;
}
-static inline int iac( void)
-{
- int ccode;
-
- __asm__ __volatile__(
- " iac 1\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : : "cc", "1" );
- return ccode;
-}
-
static inline int rchp(int chpid)
{
+ register unsigned int reg1 asm ("1") = chpid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " rchp\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (chpid)
- : "cc", "1" );
+ asm volatile(
+ " lr 1,%1\n"
+ " rchp\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 124569362f0..49bb9e371c3 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -274,12 +274,11 @@ do_sqbs(unsigned long sch, unsigned char state, int queue,
register unsigned long _sch asm ("1") = sch;
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
- asm volatile (
- " .insn rsy,0xeb000000008A,%1,0,0(%2)\n\t"
- : "+d" (_ccq), "+d" (_queuestart)
- : "d" ((unsigned long)state), "d" (_sch)
- : "memory", "cc"
- );
+ asm volatile(
+ " .insn rsy,0xeb000000008A,%1,0,0(%2)"
+ : "+d" (_ccq), "+d" (_queuestart)
+ : "d" ((unsigned long)state), "d" (_sch)
+ : "memory", "cc");
*count = _ccq & 0xff;
*start = _queuestart & 0xff;
@@ -299,12 +298,11 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue,
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
unsigned long _state = 0;
- asm volatile (
- " .insn rrf,0xB99c0000,%1,%2,0,0 \n\t"
- : "+d" (_ccq), "+d" (_queuestart), "+d" (_state)
- : "d" (_sch)
- : "memory", "cc"
- );
+ asm volatile(
+ " .insn rrf,0xB99c0000,%1,%2,0,0"
+ : "+d" (_ccq), "+d" (_queuestart), "+d" (_state)
+ : "d" (_sch)
+ : "memory", "cc" );
*count = _ccq & 0xff;
*start = _queuestart & 0xff;
*state = _state & 0xff;
@@ -319,69 +317,35 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue,
static inline int
do_siga_sync(struct subchannel_id schid, unsigned int mask1, unsigned int mask2)
{
+ register unsigned long reg0 asm ("0") = 2;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ register unsigned long reg2 asm ("2") = mask1;
+ register unsigned long reg3 asm ("3") = mask2;
int cc;
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,2 \n\t"
- "lr 1,%1 \n\t"
- "lr 2,%2 \n\t"
- "lr 3,%3 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc)
- : "d" (schid), "d" (mask1), "d" (mask2)
- : "cc", "0", "1", "2", "3"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 0,2 \n\t"
- "llgfr 1,%1 \n\t"
- "llgfr 2,%2 \n\t"
- "llgfr 3,%3 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- : "=d" (cc)
- : "d" (schid), "d" (mask1), "d" (mask2)
- : "cc", "0", "1", "2", "3"
- );
-#endif /* CONFIG_64BIT */
+ : "d" (reg0), "d" (reg1), "d" (reg2), "d" (reg3) : "cc");
return cc;
}
static inline int
do_siga_input(struct subchannel_id schid, unsigned int mask)
{
+ register unsigned long reg0 asm ("0") = 1;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ register unsigned long reg2 asm ("2") = mask;
int cc;
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,1 \n\t"
- "lr 1,%1 \n\t"
- "lr 2,%2 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- : "=d" (cc)
- : "d" (schid), "d" (mask)
- : "cc", "0", "1", "2", "memory"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 0,1 \n\t"
- "llgfr 1,%1 \n\t"
- "llgfr 2,%2 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc)
- : "d" (schid), "d" (mask)
- : "cc", "0", "1", "2", "memory"
- );
-#endif /* CONFIG_64BIT */
-
+ : "d" (reg0), "d" (reg1), "d" (reg2) : "cc", "memory");
return cc;
}
@@ -389,93 +353,35 @@ static inline int
do_siga_output(unsigned long schid, unsigned long mask, __u32 *bb,
unsigned int fc)
{
+ register unsigned long __fc asm("0") = fc;
+ register unsigned long __schid asm("1") = schid;
+ register unsigned long __mask asm("2") = mask;
int cc;
- __u32 busy_bit;
-
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,0 \n\t"
- "lr 1,%2 \n\t"
- "lr 2,%3 \n\t"
- "siga 0 \n\t"
- "0:"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- "srl 0,31 \n\t"
- "lr %1,0 \n\t"
- "1: \n\t"
- ".section .fixup,\"ax\"\n\t"
- "2: \n\t"
- "lhi %0,%4 \n\t"
- "bras 1,3f \n\t"
- ".long 1b \n\t"
- "3: \n\t"
- "l 1,0(1) \n\t"
- "br 1 \n\t"
- ".previous \n\t"
- ".section __ex_table,\"a\"\n\t"
- ".align 4 \n\t"
- ".long 0b,2b \n\t"
- ".previous \n\t"
- : "=d" (cc), "=d" (busy_bit)
- : "d" (schid), "d" (mask),
- "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
- : "cc", "0", "1", "2", "memory"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "llgfr 0,%5 \n\t"
- "lgr 1,%2 \n\t"
- "llgfr 2,%3 \n\t"
- "siga 0 \n\t"
- "0:"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- "srl 0,31 \n\t"
- "llgfr %1,0 \n\t"
- "1: \n\t"
- ".section .fixup,\"ax\"\n\t"
- "lghi %0,%4 \n\t"
- "jg 1b \n\t"
- ".previous\n\t"
- ".section __ex_table,\"a\"\n\t"
- ".align 8 \n\t"
- ".quad 0b,1b \n\t"
- ".previous \n\t"
- : "=d" (cc), "=d" (busy_bit)
- : "d" (schid), "d" (mask),
- "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION), "d" (fc)
- : "cc", "0", "1", "2", "memory"
- );
-#endif /* CONFIG_64BIT */
-
- (*bb) = busy_bit;
+
+ asm volatile(
+ " siga 0\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask)
+ : "0" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
+ : "cc", "memory");
+ (*bb) = ((unsigned int) __fc) >> 31;
return cc;
}
static inline unsigned long
do_clear_global_summary(void)
{
-
- unsigned long time;
-
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 1,3 \n\t"
- ".insn rre,0xb2650000,2,0 \n\t"
- "lr %0,3 \n\t"
- : "=d" (time) : : "cc", "1", "2", "3"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 1,3 \n\t"
- ".insn rre,0xb2650000,2,0 \n\t"
- "lgr %0,3 \n\t"
- : "=d" (time) : : "cc", "1", "2", "3"
- );
-#endif /* CONFIG_64BIT */
-
- return time;
+ register unsigned long __fn asm("1") = 3;
+ register unsigned long __tmp asm("2");
+ register unsigned long __time asm("3");
+
+ asm volatile(
+ " .insn rre,0xb2650000,2,0"
+ : "+d" (__fn), "=d" (__tmp), "=d" (__time));
+ return __time;
}
/*
diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c
index 821dde86e24..809dd8d7f47 100644
--- a/drivers/s390/net/iucv.c
+++ b/drivers/s390/net/iucv.c
@@ -534,19 +534,15 @@ iucv_add_handler (handler *new)
*
* Returns: return code from CP's IUCV call
*/
-static __inline__ ulong
-b2f0(__u32 code, void *parm)
+static inline ulong b2f0(__u32 code, void *parm)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iucv_dumpit("iparml before b2f0 call:", parm, sizeof(iucv_param));
- asm volatile (
- "LRA 1,0(%1)\n\t"
- "LR 0,%0\n\t"
- ".long 0xb2f01000"
- :
- : "d" (code), "a" (parm)
- : "0", "1"
- );
+ reg0 = code;
+ reg1 = virt_to_phys(parm);
+ asm volatile(".long 0xb2f01000" : : "d" (reg0), "a" (reg1));
iucv_dumpit("iparml after b2f0 call:", parm, sizeof(iucv_param));
@@ -1248,6 +1244,8 @@ iucv_purge (__u16 pathid, __u32 msgid, __u32 srccls, __u32 *audit)
static int
iucv_query_generic(int want_maxconn)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iparml_purge *parm = (iparml_purge *)grab_param();
int bufsize, maxconn;
int ccode;
@@ -1256,18 +1254,15 @@ iucv_query_generic(int want_maxconn)
* Call b2f0 and store R0 (max buffer size),
* R1 (max connections) and CC.
*/
- asm volatile (
- "LRA 1,0(%4)\n\t"
- "LR 0,%3\n\t"
- ".long 0xb2f01000\n\t"
- "IPM %0\n\t"
- "SRL %0,28\n\t"
- "ST 0,%1\n\t"
- "ST 1,%2\n\t"
- : "=d" (ccode), "=m" (bufsize), "=m" (maxconn)
- : "d" (QUERY), "a" (parm)
- : "0", "1", "cc"
- );
+ reg0 = QUERY;
+ reg1 = virt_to_phys(parm);
+ asm volatile(
+ " .long 0xb2f01000\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc");
+ bufsize = reg0;
+ maxconn = reg1;
release_param(parm);
if (ccode)
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
index 5613b4564fa..8364d5475ac 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -8067,7 +8067,7 @@ qeth_arp_constructor(struct neighbour *neigh)
neigh->parms = neigh_parms_clone(parms);
rcu_read_unlock();
- neigh->type = inet_addr_type(*(u32 *) neigh->primary_key);
+ neigh->type = inet_addr_type(*(__be32 *) neigh->primary_key);
neigh->nud_state = NUD_NOARP;
neigh->ops = arp_direct_ops;
neigh->output = neigh->ops->queue_xmit;
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index a914129a4da..479364d0332 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -253,11 +253,12 @@ s390_revalidate_registers(struct mci *mci)
kill_task = 1;
#ifndef CONFIG_64BIT
- asm volatile("ld 0,0(%0)\n"
- "ld 2,8(%0)\n"
- "ld 4,16(%0)\n"
- "ld 6,24(%0)"
- : : "a" (&S390_lowcore.floating_pt_save_area));
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 2,8(%0)\n"
+ " ld 4,16(%0)\n"
+ " ld 6,24(%0)"
+ : : "a" (&S390_lowcore.floating_pt_save_area));
#endif
if (MACHINE_HAS_IEEE) {
@@ -274,37 +275,36 @@ s390_revalidate_registers(struct mci *mci)
* Floating point control register can't be restored.
* Task will be terminated.
*/
- asm volatile ("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
+ asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
kill_task = 1;
- }
- else
- asm volatile (
- "lfpc 0(%0)"
- : : "a" (fpt_creg_save_area));
-
- asm volatile("ld 0,0(%0)\n"
- "ld 1,8(%0)\n"
- "ld 2,16(%0)\n"
- "ld 3,24(%0)\n"
- "ld 4,32(%0)\n"
- "ld 5,40(%0)\n"
- "ld 6,48(%0)\n"
- "ld 7,56(%0)\n"
- "ld 8,64(%0)\n"
- "ld 9,72(%0)\n"
- "ld 10,80(%0)\n"
- "ld 11,88(%0)\n"
- "ld 12,96(%0)\n"
- "ld 13,104(%0)\n"
- "ld 14,112(%0)\n"
- "ld 15,120(%0)\n"
- : : "a" (fpt_save_area));
+ } else
+ asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
+
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 1,8(%0)\n"
+ " ld 2,16(%0)\n"
+ " ld 3,24(%0)\n"
+ " ld 4,32(%0)\n"
+ " ld 5,40(%0)\n"
+ " ld 6,48(%0)\n"
+ " ld 7,56(%0)\n"
+ " ld 8,64(%0)\n"
+ " ld 9,72(%0)\n"
+ " ld 10,80(%0)\n"
+ " ld 11,88(%0)\n"
+ " ld 12,96(%0)\n"
+ " ld 13,104(%0)\n"
+ " ld 14,112(%0)\n"
+ " ld 15,120(%0)\n"
+ : : "a" (fpt_save_area));
}
/* Revalidate access registers */
- asm volatile("lam 0,15,0(%0)"
- : : "a" (&S390_lowcore.access_regs_save_area));
+ asm volatile(
+ " lam 0,15,0(%0)"
+ : : "a" (&S390_lowcore.access_regs_save_area));
if (!mci->ar)
/*
* Access registers have unknown contents.
@@ -321,11 +321,13 @@ s390_revalidate_registers(struct mci *mci)
s390_handle_damage("invalid control registers.");
else
#ifdef CONFIG_64BIT
- asm volatile("lctlg 0,15,0(%0)"
- : : "a" (&S390_lowcore.cregs_save_area));
+ asm volatile(
+ " lctlg 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
#else
- asm volatile("lctl 0,15,0(%0)"
- : : "a" (&S390_lowcore.cregs_save_area));
+ asm volatile(
+ " lctl 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
#endif
/*
@@ -339,20 +341,23 @@ s390_revalidate_registers(struct mci *mci)
* old contents (should be zero) otherwise set it to zero.
*/
if (!mci->pr)
- asm volatile("sr 0,0\n"
- "sckpf"
- : : : "0", "cc");
+ asm volatile(
+ " sr 0,0\n"
+ " sckpf"
+ : : : "0", "cc");
else
asm volatile(
- "l 0,0(%0)\n"
- "sckpf"
- : : "a" (&S390_lowcore.tod_progreg_save_area) : "0", "cc");
+ " l 0,0(%0)\n"
+ " sckpf"
+ : : "a" (&S390_lowcore.tod_progreg_save_area)
+ : "0", "cc");
#endif
/* Revalidate clock comparator register */
- asm volatile ("stck 0(%1)\n"
- "sckc 0(%1)"
- : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
+ asm volatile(
+ " stck 0(%1)\n"
+ " sckc 0(%1)"
+ : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
/* Check if old PSW is valid */
if (!mci->wp)
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 0e4a7ebe300..6b35ed8301e 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -681,6 +681,7 @@ static struct eisa_device_id aha1740_ids[] = {
{ "ADP0400" }, /* 1744 */
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, aha1740_ids);
static struct eisa_driver aha1740_driver = {
.id_table = aha1740_ids,
diff --git a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c
index 867cbe23579..1ac119733ba 100644
--- a/drivers/scsi/aic7xxx/aic7770_osm.c
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -132,7 +132,8 @@ static struct eisa_device_id aic7770_ids[] = {
{ "ADP7770", 5 }, /* AIC7770 generic */
{ "" }
};
-
+MODULE_DEVICE_TABLE(eisa, aic7770_ids);
+
static struct eisa_driver aic7770_driver = {
.id_table = aic7770_ids,
.driver = {
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index 592b52afe65..683fc7ae4b8 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -1756,16 +1756,23 @@ static void set_mesh_power(struct mesh_state *ms, int state)
pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
msleep(10);
}
-}
+}
#ifdef CONFIG_PM
-static int mesh_suspend(struct macio_dev *mdev, pm_message_t state)
+static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
{
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
unsigned long flags;
- if (state.event == mdev->ofdev.dev.power.power_state.event || state.event < 2)
+ switch (mesg.event) {
+ case PM_EVENT_SUSPEND:
+ case PM_EVENT_FREEZE:
+ break;
+ default:
+ return 0;
+ }
+ if (mesg.event == mdev->ofdev.dev.power.power_state.event)
return 0;
scsi_block_requests(ms->host);
@@ -1780,7 +1787,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t state)
disable_irq(ms->meshintr);
set_mesh_power(ms, 0);
- mdev->ofdev.dev.power.power_state = state;
+ mdev->ofdev.dev.power.power_state = mesg;
return 0;
}
diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c
index b27e85428da..551baccec52 100644
--- a/drivers/scsi/sim710.c
+++ b/drivers/scsi/sim710.c
@@ -282,6 +282,7 @@ static struct eisa_device_id sim710_eisa_ids[] = {
{ "HWP0C80" },
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, sim710_eisa_ids);
static __init int
sim710_eisa_probe(struct device *dev)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 5b48ac22c9c..d926272a40d 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -295,7 +295,7 @@ config SERIAL_AMBA_PL011_CONSOLE
Even if you say Y here, the currently visible framebuffer console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
- "console=ttyAM0". (Try "man bootparam" or see the documentation of
+ "console=ttyAMA0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
@@ -642,12 +642,17 @@ config V850E_UART_CONSOLE
select SERIAL_CORE_CONSOLE
config SERIAL_SH_SCI
- tristate "SH SCI(F) serial port support"
+ tristate "SuperH SCI(F) serial port support"
depends on SUPERH || H8300
select SERIAL_CORE
+config SERIAL_SH_SCI_NR_UARTS
+ int "Maximum number of SCI(F) serial ports"
+ depends on SERIAL_SH_SCI
+ default "2"
+
config SERIAL_SH_SCI_CONSOLE
- bool "Support for console on SH SCI(F)"
+ bool "Support for console on SuperH SCI(F)"
depends on SERIAL_SH_SCI=y
select SERIAL_CORE_CONSOLE
diff --git a/drivers/serial/at91_serial.c b/drivers/serial/at91_serial.c
index 54c6b2adf7b..bf4bf103e5a 100644
--- a/drivers/serial/at91_serial.c
+++ b/drivers/serial/at91_serial.c
@@ -139,7 +139,7 @@ static void at91_set_mctrl(struct uart_port *port, u_int mctrl)
* AT91RM9200 Errata #39: RTS0 is not internally connected to PA21.
* We need to drive the pin manually.
*/
- if (port->mapbase == AT91_BASE_US0) {
+ if (port->mapbase == AT91RM9200_BASE_US0) {
if (mctrl & TIOCM_RTS)
at91_set_gpio_value(AT91_PIN_PA21, 0);
else
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 372e47f7d59..5f7ba1adb30 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1929,6 +1929,13 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
mutex_lock(&state->mutex);
+#ifdef CONFIG_DISABLE_CONSOLE_SUSPEND
+ if (uart_console(port)) {
+ mutex_unlock(&state->mutex);
+ return 0;
+ }
+#endif
+
if (state->info && state->info->flags & UIF_INITIALIZED) {
const struct uart_ops *ops = port->ops;
@@ -1967,6 +1974,13 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
mutex_lock(&state->mutex);
+#ifdef CONFIG_DISABLE_CONSOLE_SUSPEND
+ if (uart_console(port)) {
+ mutex_unlock(&state->mutex);
+ return 0;
+ }
+#endif
+
uart_change_pm(state, 0);
/*
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index cbede06cac2..f336ba6778d 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -3,7 +3,7 @@
*
* SuperH on-chip serial module support. (SCI with no FIFO / with FIFO)
*
- * Copyright (C) 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2002 - 2006 Paul Mundt
*
* based off of the old drivers/char/sh-sci.c by:
*
@@ -20,10 +20,9 @@
#undef DEBUG
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
@@ -32,71 +31,77 @@
#include <linux/major.h>
#include <linux/string.h>
#include <linux/sysrq.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
-#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/console.h>
-#include <linux/bitops.h>
-#include <linux/generic_serial.h>
+#include <linux/platform_device.h>
#ifdef CONFIG_CPU_FREQ
#include <linux/notifier.h>
#include <linux/cpufreq.h>
#endif
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
#include <asm/clock.h>
-#endif
-
-#ifdef CONFIG_SH_STANDARD_BIOS
#include <asm/sh_bios.h>
+#include <asm/kgdb.h>
#endif
+#include <asm/sci.h>
+
#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include "sh-sci.h"
-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
+struct sci_port {
+ struct uart_port port;
+
+ /* Port type */
+ unsigned int type;
+
+ /* Port IRQs: ERI, RXI, TXI, BRI (optional) */
+ unsigned int irqs[SCIx_NR_IRQS];
+
+ /* Port pin configuration */
+ void (*init_pins)(struct uart_port *port,
+ unsigned int cflag);
-static int kgdb_get_char(struct sci_port *port);
-static void kgdb_put_char(struct sci_port *port, char c);
-static void kgdb_handle_error(struct sci_port *port);
+ /* Port enable callback */
+ void (*enable)(struct uart_port *port);
+
+ /* Port disable callback */
+ void (*disable)(struct uart_port *port);
+
+ /* Break timer */
+ struct timer_list break_timer;
+ int break_flag;
+};
+
+#ifdef CONFIG_SH_KGDB
static struct sci_port *kgdb_sci_port;
-#endif /* CONFIG_SH_KGDB */
+#endif
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
-static struct sci_port *serial_console_port = 0;
-#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
+static struct sci_port *serial_console_port;
+#endif
/* Function prototypes */
static void sci_stop_tx(struct uart_port *port);
-static void sci_start_tx(struct uart_port *port);
-static void sci_start_rx(struct uart_port *port, unsigned int tty_start);
-static void sci_stop_rx(struct uart_port *port);
-static int sci_request_irq(struct sci_port *port);
-static void sci_free_irq(struct sci_port *port);
-
-static struct sci_port sci_ports[];
-static struct uart_driver sci_uart_driver;
-#define SCI_NPORTS sci_uart_driver.nr
+#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+static struct sci_port sci_ports[SCI_NPORTS];
+static struct uart_driver sci_uart_driver;
-static void handle_error(struct uart_port *port)
-{ /* Clear error flags */
+#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && \
+ defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+static inline void handle_error(struct uart_port *port)
+{
+ /* Clear error flags */
sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
}
@@ -106,8 +111,8 @@ static int get_char(struct uart_port *port)
unsigned short status;
int c;
- local_irq_save(flags);
- do {
+ spin_lock_irqsave(&port->lock, flags);
+ do {
status = sci_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) {
handle_error(port);
@@ -117,38 +122,19 @@ static int get_char(struct uart_port *port)
c = sci_in(port, SCxRDR);
sci_in(port, SCxSR); /* Dummy read */
sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
return c;
}
-
-/* Taken from sh-stub.c of GDB 4.18 */
-static const char hexchars[] = "0123456789abcdef";
-
-static __inline__ char highhex(int x)
-{
- return hexchars[(x >> 4) & 0xf];
-}
-
-static __inline__ char lowhex(int x)
-{
- return hexchars[x & 0xf];
-}
-
#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
-/*
- * Send the packet in buffer. The host gets one chance to read it.
- * This routine does not wait for a positive acknowledge.
- */
-
-#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
+#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || defined(CONFIG_SH_KGDB)
static void put_char(struct uart_port *port, char c)
{
unsigned long flags;
unsigned short status;
- local_irq_save(flags);
+ spin_lock_irqsave(&port->lock, flags);
do {
status = sci_in(port, SCxSR);
@@ -158,9 +144,11 @@ static void put_char(struct uart_port *port, char c)
sci_in(port, SCxSR); /* Dummy read */
sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
}
+#endif
+#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static void put_string(struct sci_port *sci_port, const char *buffer, int count)
{
struct uart_port *port = &sci_port->port;
@@ -213,96 +201,28 @@ static void put_string(struct sci_port *sci_port, const char *buffer, int count)
}
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
-
#ifdef CONFIG_SH_KGDB
-
-/* Is the SCI ready, ie is there a char waiting? */
-static int kgdb_is_char_ready(struct sci_port *port)
-{
- unsigned short status = sci_in(port, SCxSR);
-
- if (status & (SCxSR_ERRORS(port) | SCxSR_BRK(port)))
- kgdb_handle_error(port);
-
- return (status & SCxSR_RDxF(port));
-}
-
-/* Write a char */
-static void kgdb_put_char(struct sci_port *port, char c)
-{
- unsigned short status;
-
- do
- status = sci_in(port, SCxSR);
- while (!(status & SCxSR_TDxE(port)));
-
- sci_out(port, SCxTDR, c);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
-}
-
-/* Get a char if there is one, else ret -1 */
-static int kgdb_get_char(struct sci_port *port)
-{
- int c;
-
- if (kgdb_is_char_ready(port) == 0)
- c = -1;
- else {
- c = sci_in(port, SCxRDR);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- }
-
- return c;
-}
-
-/* Called from kgdbstub.c to get a character, i.e. is blocking */
static int kgdb_sci_getchar(void)
{
- volatile int c;
+ int c;
/* Keep trying to read a character, this could be neater */
- while ((c = kgdb_get_char(kgdb_sci_port)) < 0);
+ while ((c = get_char(kgdb_sci_port)) < 0)
+ cpu_relax();
return c;
}
-/* Called from kgdbstub.c to put a character, just a wrapper */
-static void kgdb_sci_putchar(int c)
-{
-
- kgdb_put_char(kgdb_sci_port, c);
-}
-
-/* Clear any errors on the SCI */
-static void kgdb_handle_error(struct sci_port *port)
+static inline void kgdb_sci_putchar(int c)
{
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); /* Clear error flags */
+ put_char(kgdb_sci_port, c);
}
-
-/* Breakpoint if there's a break sent on the serial port */
-static void kgdb_break_interrupt(int irq, void *ptr, struct pt_regs *regs)
-{
- struct sci_port *port = ptr;
- unsigned short status = sci_in(port, SCxSR);
-
- if (status & SCxSR_BRK(port)) {
-
- /* Break into the debugger if a break is detected */
- BREAKPOINT();
-
- /* Clear */
- sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
- }
-}
-
#endif /* CONFIG_SH_KGDB */
#if defined(__H8300S__)
enum { sci_disable, sci_enable };
-static void h8300_sci_enable(struct uart_port* port, unsigned int ctrl)
+static void h8300_sci_config(struct uart_port* port, unsigned int ctrl)
{
volatile unsigned char *mstpcrl=(volatile unsigned char *)MSTPCRL;
int ch = (port->mapbase - SMR0) >> 3;
@@ -314,32 +234,66 @@ static void h8300_sci_enable(struct uart_port* port, unsigned int ctrl)
*mstpcrl &= ~mask;
}
}
+
+static inline void h8300_sci_enable(struct uart_port *port)
+{
+ h8300_sci_config(port, sci_enable);
+}
+
+static inline void h8300_sci_disable(struct uart_port *port)
+{
+ h8300_sci_config(port, sci_disable);
+}
#endif
-#if defined(SCI_ONLY) || defined(SCI_AND_SCIF)
-#if defined(__H8300H__) || defined(__H8300S__)
+#if defined(SCI_ONLY) || defined(SCI_AND_SCIF) && \
+ defined(__H8300H__) || defined(__H8300S__)
static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag)
{
int ch = (port->mapbase - SMR0) >> 3;
/* set DDR regs */
- H8300_GPIO_DDR(h8300_sci_pins[ch].port,h8300_sci_pins[ch].rx,H8300_GPIO_INPUT);
- H8300_GPIO_DDR(h8300_sci_pins[ch].port,h8300_sci_pins[ch].tx,H8300_GPIO_OUTPUT);
+ H8300_GPIO_DDR(h8300_sci_pins[ch].port,
+ h8300_sci_pins[ch].rx,
+ H8300_GPIO_INPUT);
+ H8300_GPIO_DDR(h8300_sci_pins[ch].port,
+ h8300_sci_pins[ch].tx,
+ H8300_GPIO_OUTPUT);
+
/* tx mark output*/
H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx;
}
+#else
+#define sci_init_pins_sci NULL
+#endif
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag)
+{
+ unsigned int fcr_val = 0;
+
+ if (cflag & CRTSCTS)
+ fcr_val |= SCFCR_MCE;
+
+ sci_out(port, SCFCR, fcr_val);
+}
+#else
+#define sci_init_pins_irda NULL
#endif
+
+#ifdef SCI_ONLY
+#define sci_init_pins_scif NULL
#endif
#if defined(SCIF_ONLY) || defined(SCI_AND_SCIF)
-#if defined(CONFIG_CPU_SUBTYPE_SH7300)
+#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7710)
/* SH7300 doesn't use RTS/CTS */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
{
sci_out(port, SCFCR, 0);
}
#elif defined(CONFIG_CPU_SH3)
-/* For SH7705, SH7707, SH7709, SH7709A, SH7729 */
+/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
{
unsigned int fcr_val = 0;
@@ -366,20 +320,7 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
sci_out(port, SCFCR, fcr_val);
}
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
-static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag)
-{
- unsigned int fcr_val = 0;
-
- if (cflag & CRTSCTS)
- fcr_val |= SCFCR_MCE;
-
- sci_out(port, SCFCR, fcr_val);
-}
-#endif
#else
-
/* For SH7750 */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
{
@@ -388,7 +329,9 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
if (cflag & CRTSCTS) {
fcr_val |= SCFCR_MCE;
} else {
-#ifdef CONFIG_CPU_SUBTYPE_SH7780
+#ifdef CONFIG_CPU_SUBTYPE_SH7343
+ /* Nothing */
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
ctrl_outw(0x0080, SCSPTR0); /* Set RTS = 1 */
#else
ctrl_outw(0x0080, SCSPTR2); /* Set RTS = 1 */
@@ -396,10 +339,41 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
}
sci_out(port, SCFCR, fcr_val);
}
+#endif
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7760) || defined(CONFIG_CPU_SUBTYPE_SH7780)
+static inline int scif_txroom(struct uart_port *port)
+{
+ return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0x7f);
+}
+
+static inline int scif_rxroom(struct uart_port *port)
+{
+ return sci_in(port, SCRFDR) & 0x7f;
+}
+#else
+static inline int scif_txroom(struct uart_port *port)
+{
+ return SCIF_TXROOM_MAX - (sci_in(port, SCFDR) >> 8);
+}
+static inline int scif_rxroom(struct uart_port *port)
+{
+ return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
+}
#endif
#endif /* SCIF_ONLY || SCI_AND_SCIF */
+static inline int sci_txroom(struct uart_port *port)
+{
+ return ((sci_in(port, SCxSR) & SCI_TDRE) != 0);
+}
+
+static inline int sci_rxroom(struct uart_port *port)
+{
+ return ((sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0);
+}
+
/* ********************************************************************** *
* the interrupt related routines *
* ********************************************************************** */
@@ -408,14 +382,12 @@ static void sci_transmit_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->info->xmit;
unsigned int stopped = uart_tx_stopped(port);
- unsigned long flags;
unsigned short status;
unsigned short ctrl;
- int count, txroom;
+ int count;
status = sci_in(port, SCxSR);
if (!(status & SCxSR_TDxE(port))) {
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
if (uart_circ_empty(xmit)) {
ctrl &= ~SCI_CTRL_FLAGS_TIE;
@@ -423,25 +395,15 @@ static void sci_transmit_chars(struct uart_port *port)
ctrl |= SCI_CTRL_FLAGS_TIE;
}
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
return;
}
-#if !defined(SCI_ONLY)
- if (port->type == PORT_SCIF) {
-#if defined(CONFIG_CPU_SUBTYPE_SH7760) || defined(CONFIG_CPU_SUBTYPE_SH7780)
- txroom = SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0x7f);
-#else
- txroom = SCIF_TXROOM_MAX - (sci_in(port, SCFDR)>>8);
-#endif
- } else {
- txroom = (sci_in(port, SCxSR) & SCI_TDRE)?1:0;
- }
-#else
- txroom = (sci_in(port, SCxSR) & SCI_TDRE)?1:0;
+#ifndef SCI_ONLY
+ if (port->type == PORT_SCIF)
+ count = scif_txroom(port);
+ else
#endif
-
- count = txroom;
+ count = sci_txroom(port);
do {
unsigned char c;
@@ -468,7 +430,6 @@ static void sci_transmit_chars(struct uart_port *port)
if (uart_circ_empty(xmit)) {
sci_stop_tx(port);
} else {
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
#if !defined(SCI_ONLY)
@@ -480,7 +441,6 @@ static void sci_transmit_chars(struct uart_port *port)
ctrl |= SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
}
@@ -490,6 +450,7 @@ static void sci_transmit_chars(struct uart_port *port)
static inline void sci_receive_chars(struct uart_port *port,
struct pt_regs *regs)
{
+ struct sci_port *sci_port = (struct sci_port *)port;
struct tty_struct *tty = port->info->tty;
int i, count, copied = 0;
unsigned short status;
@@ -501,18 +462,11 @@ static inline void sci_receive_chars(struct uart_port *port,
while (1) {
#if !defined(SCI_ONLY)
- if (port->type == PORT_SCIF) {
-#if defined(CONFIG_CPU_SUBTYPE_SH7760) || defined(CONFIG_CPU_SUBTYPE_SH7780)
- count = sci_in(port, SCRFDR) & 0x7f;
-#else
- count = sci_in(port, SCFDR)&SCIF_RFDC_MASK ;
-#endif
- } else {
- count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0;
- }
-#else
- count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0;
+ if (port->type == PORT_SCIF)
+ count = scif_rxroom(port);
+ else
#endif
+ count = sci_rxroom(port);
/* Don't copy more bytes than there is room for in the buffer */
count = tty_buffer_request_room(tty, count);
@@ -523,11 +477,10 @@ static inline void sci_receive_chars(struct uart_port *port,
if (port->type == PORT_SCI) {
char c = sci_in(port, SCxRDR);
- if(((struct sci_port *)port)->break_flag
- || uart_handle_sysrq_char(port, c, regs)) {
+ if (uart_handle_sysrq_char(port, c, regs) || sci_port->break_flag)
count = 0;
- } else {
- tty_insert_flip_char(tty, c, TTY_NORMAL);
+ else {
+ tty_insert_flip_char(tty, c, TTY_NORMAL);
}
} else {
for (i=0; i<count; i++) {
@@ -535,15 +488,17 @@ static inline void sci_receive_chars(struct uart_port *port,
status = sci_in(port, SCxSR);
#if defined(CONFIG_CPU_SH3)
/* Skip "chars" during break */
- if (((struct sci_port *)port)->break_flag) {
+ if (sci_port->break_flag) {
if ((c == 0) &&
(status & SCxSR_FER(port))) {
count--; i--;
continue;
}
+
/* Nonzero => end-of-break */
pr_debug("scif: debounce<%02x>\n", c);
- ((struct sci_port *)port)->break_flag = 0;
+ sci_port->break_flag = 0;
+
if (STEPFN(c)) {
count--; i--;
continue;
@@ -600,15 +555,17 @@ static void sci_schedule_break_timer(struct sci_port *port)
/* Ensure that two consecutive samples find the break over. */
static void sci_break_timer(unsigned long data)
{
- struct sci_port * port = (struct sci_port *)data;
- if(sci_rxd_in(&port->port) == 0) {
+ struct sci_port *port = (struct sci_port *)data;
+
+ if (sci_rxd_in(&port->port) == 0) {
port->break_flag = 1;
- sci_schedule_break_timer(port);
- } else if(port->break_flag == 1){
+ sci_schedule_break_timer(port);
+ } else if (port->break_flag == 1) {
/* break is over. */
port->break_flag = 2;
- sci_schedule_break_timer(port);
- } else port->break_flag = 0;
+ sci_schedule_break_timer(port);
+ } else
+ port->break_flag = 0;
}
static inline int sci_handle_errors(struct uart_port *port)
@@ -617,40 +574,41 @@ static inline int sci_handle_errors(struct uart_port *port)
unsigned short status = sci_in(port, SCxSR);
struct tty_struct *tty = port->info->tty;
- if (status&SCxSR_ORER(port)) {
+ if (status & SCxSR_ORER(port)) {
/* overrun error */
- if(tty_insert_flip_char(tty, 0, TTY_OVERRUN))
+ if (tty_insert_flip_char(tty, 0, TTY_OVERRUN))
copied++;
pr_debug("sci: overrun error\n");
}
- if (status&SCxSR_FER(port)) {
+ if (status & SCxSR_FER(port)) {
if (sci_rxd_in(port) == 0) {
/* Notify of BREAK */
- struct sci_port * sci_port = (struct sci_port *)port;
- if(!sci_port->break_flag) {
- sci_port->break_flag = 1;
- sci_schedule_break_timer((struct sci_port *)port);
+ struct sci_port *sci_port = (struct sci_port *)port;
+
+ if (!sci_port->break_flag) {
+ sci_port->break_flag = 1;
+ sci_schedule_break_timer(sci_port);
+
/* Do sysrq handling. */
- if(uart_handle_break(port))
+ if (uart_handle_break(port))
return 0;
pr_debug("sci: BREAK detected\n");
- if(tty_insert_flip_char(tty, 0, TTY_BREAK))
+ if (tty_insert_flip_char(tty, 0, TTY_BREAK))
copied++;
}
- }
- else {
+ } else {
/* frame error */
- if(tty_insert_flip_char(tty, 0, TTY_FRAME))
+ if (tty_insert_flip_char(tty, 0, TTY_FRAME))
copied++;
pr_debug("sci: frame error\n");
}
}
- if (status&SCxSR_PER(port)) {
- if(tty_insert_flip_char(tty, 0, TTY_PARITY))
- copied++;
+ if (status & SCxSR_PER(port)) {
/* parity error */
+ if (tty_insert_flip_char(tty, 0, TTY_PARITY))
+ copied++;
pr_debug("sci: parity error\n");
}
@@ -673,7 +631,7 @@ static inline int sci_handle_breaks(struct uart_port *port)
s->break_flag = 1;
#endif
/* Notify of BREAK */
- if(tty_insert_flip_char(tty, 0, TTY_BREAK))
+ if (tty_insert_flip_char(tty, 0, TTY_BREAK))
copied++;
pr_debug("sci: BREAK detected\n");
}
@@ -682,7 +640,7 @@ static inline int sci_handle_breaks(struct uart_port *port)
/* XXX: Handle SCIF overrun error */
if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) {
sci_out(port, SCLSR, 0);
- if(tty_insert_flip_char(tty, 0, TTY_OVERRUN)) {
+ if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) {
copied++;
pr_debug("sci: overrun error\n");
}
@@ -691,13 +649,12 @@ static inline int sci_handle_breaks(struct uart_port *port)
if (copied)
tty_flip_buffer_push(tty);
+
return copied;
}
-static irqreturn_t sci_rx_interrupt(int irq, void *ptr, struct pt_regs *regs)
+static irqreturn_t sci_rx_interrupt(int irq, void *port, struct pt_regs *regs)
{
- struct uart_port *port = ptr;
-
/* I think sci_receive_chars has to be called irrespective
* of whether the I_IXOFF is set, otherwise, how is the interrupt
* to be disabled?
@@ -711,7 +668,9 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr, struct pt_regs *regs)
{
struct uart_port *port = ptr;
+ spin_lock_irq(&port->lock);
sci_transmit_chars(port);
+ spin_unlock_irq(&port->lock);
return IRQ_HANDLED;
}
@@ -755,6 +714,12 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr, struct pt_regs *regs)
/* Handle BREAKs */
sci_handle_breaks(port);
+
+#ifdef CONFIG_SH_KGDB
+ /* Break into the debugger if a break is detected */
+ BREAKPOINT();
+#endif
+
sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
return IRQ_HANDLED;
@@ -769,16 +734,16 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr, struct pt_regs *regs)
scr_status = sci_in(port,SCSCR);
/* Tx Interrupt */
- if ((ssr_status&0x0020) && (scr_status&0x0080))
+ if ((ssr_status & 0x0020) && (scr_status & 0x0080))
sci_tx_interrupt(irq, ptr, regs);
/* Rx Interrupt */
- if ((ssr_status&0x0002) && (scr_status&0x0040))
+ if ((ssr_status & 0x0002) && (scr_status & 0x0040))
sci_rx_interrupt(irq, ptr, regs);
/* Error Interrupt */
- if ((ssr_status&0x0080) && (scr_status&0x0400))
+ if ((ssr_status & 0x0080) && (scr_status & 0x0400))
sci_er_interrupt(irq, ptr, regs);
/* Break Interrupt */
- if ((ssr_status&0x0010) && (scr_status&0x0200))
+ if ((ssr_status & 0x0010) && (scr_status & 0x0200))
sci_br_interrupt(irq, ptr, regs);
return IRQ_HANDLED;
@@ -789,7 +754,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr, struct pt_regs *regs)
* Here we define a transistion notifier so that we can update all of our
* ports' baud rate when the peripheral clock changes.
*/
-static int sci_notifier(struct notifier_block *self, unsigned long phase, void *p)
+static int sci_notifier(struct notifier_block *self,
+ unsigned long phase, void *p)
{
struct cpufreq_freqs *freqs = p;
int i;
@@ -816,8 +782,9 @@ static int sci_notifier(struct notifier_block *self, unsigned long phase, void *
clk_put(clk);
}
- printk("%s: got a postchange notification for cpu %d (old %d, new %d)\n",
- __FUNCTION__, freqs->cpu, freqs->old, freqs->new);
+ printk(KERN_INFO "%s: got a postchange notification "
+ "for cpu %d (old %d, new %d)\n",
+ __FUNCTION__, freqs->cpu, freqs->old, freqs->new);
}
return NOTIFY_OK;
@@ -841,8 +808,9 @@ static int sci_request_irq(struct sci_port *port)
printk(KERN_ERR "sci: Cannot allocate irq.(IRQ=0)\n");
return -ENODEV;
}
- if (request_irq(port->irqs[0], sci_mpxed_interrupt, IRQF_DISABLED,
- "sci", port)) {
+
+ if (request_irq(port->irqs[0], sci_mpxed_interrupt,
+ SA_INTERRUPT, "sci", port)) {
printk(KERN_ERR "sci: Cannot allocate irq.\n");
return -ENODEV;
}
@@ -850,8 +818,8 @@ static int sci_request_irq(struct sci_port *port)
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
if (!port->irqs[i])
continue;
- if (request_irq(port->irqs[i], handlers[i], IRQF_DISABLED,
- desc[i], port)) {
+ if (request_irq(port->irqs[i], handlers[i],
+ SA_INTERRUPT, desc[i], port)) {
printk(KERN_ERR "sci: Cannot allocate irq.\n");
return -ENODEV;
}
@@ -903,50 +871,42 @@ static unsigned int sci_get_mctrl(struct uart_port *port)
static void sci_start_tx(struct uart_port *port)
{
- struct sci_port *s = &sci_ports[port->line];
+ unsigned short ctrl;
- disable_irq(s->irqs[SCIx_TXI_IRQ]);
- sci_transmit_chars(port);
- enable_irq(s->irqs[SCIx_TXI_IRQ]);
+ /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
+ ctrl = sci_in(port, SCSCR);
+ ctrl |= SCI_CTRL_FLAGS_TIE;
+ sci_out(port, SCSCR, ctrl);
}
static void sci_stop_tx(struct uart_port *port)
{
- unsigned long flags;
unsigned short ctrl;
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
ctrl &= ~SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
static void sci_start_rx(struct uart_port *port, unsigned int tty_start)
{
- unsigned long flags;
unsigned short ctrl;
/* Set RIE (Receive Interrupt Enable) bit in SCSCR */
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
ctrl |= SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE;
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
static void sci_stop_rx(struct uart_port *port)
{
- unsigned long flags;
unsigned short ctrl;
/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
static void sci_enable_ms(struct uart_port *port)
@@ -963,9 +923,8 @@ static int sci_startup(struct uart_port *port)
{
struct sci_port *s = &sci_ports[port->line];
-#if defined(__H8300S__)
- h8300_sci_enable(port, sci_enable);
-#endif
+ if (s->enable)
+ s->enable(port);
sci_request_irq(s);
sci_start_tx(port);
@@ -982,9 +941,8 @@ static void sci_shutdown(struct uart_port *port)
sci_stop_tx(port);
sci_free_irq(s);
-#if defined(__H8300S__)
- h8300_sci_enable(port, sci_disable);
-#endif
+ if (s->disable)
+ s->disable(port);
}
static void sci_set_termios(struct uart_port *port, struct termios *termios,
@@ -997,6 +955,23 @@ static void sci_set_termios(struct uart_port *port, struct termios *termios,
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ switch (baud) {
+ case 0:
+ t = -1;
+ break;
+ default:
+ {
+#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
+ struct clk *clk = clk_get("module_clk");
+ t = SCBRR_VALUE(baud, clk_get_rate(clk));
+ clk_put(clk);
+#else
+ t = SCBRR_VALUE(baud);
+#endif
+ }
+ break;
+ }
+
spin_lock_irqsave(&port->lock, flags);
do {
@@ -1006,9 +981,8 @@ static void sci_set_termios(struct uart_port *port, struct termios *termios,
sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */
#if !defined(SCI_ONLY)
- if (port->type == PORT_SCIF) {
+ if (port->type == PORT_SCIF)
sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST);
- }
#endif
smr_val = sci_in(port, SCSMR) & 3;
@@ -1025,23 +999,6 @@ static void sci_set_termios(struct uart_port *port, struct termios *termios,
sci_out(port, SCSMR, smr_val);
- switch (baud) {
- case 0:
- t = -1;
- break;
- default:
- {
-#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
- struct clk *clk = clk_get("module_clk");
- t = SCBRR_VALUE(baud, clk_get_rate(clk));
- clk_put(clk);
-#else
- t = SCBRR_VALUE(baud);
-#endif
- }
- break;
- }
-
if (t > 0) {
if(t >= 256) {
sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1);
@@ -1092,11 +1049,23 @@ static void sci_config_port(struct uart_port *port, int flags)
port->type = s->type;
+ switch (port->type) {
+ case PORT_SCI:
+ s->init_pins = sci_init_pins_sci;
+ break;
+ case PORT_SCIF:
+ s->init_pins = sci_init_pins_scif;
+ break;
+ case PORT_IRDA:
+ s->init_pins = sci_init_pins_irda;
+ break;
+ }
+
#if defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
if (port->mapbase == 0)
port->mapbase = onchip_remap(SCIF_ADDR_SH5, 1024, "SCIF");
- port->membase = (void *)port->mapbase;
+ port->membase = (void __iomem *)port->mapbase;
#endif
}
@@ -1132,412 +1101,61 @@ static struct uart_ops sci_uart_ops = {
.verify_port = sci_verify_port,
};
-static struct sci_port sci_ports[] = {
-#if defined(CONFIG_CPU_SUBTYPE_SH7708)
- {
- .port = {
- .membase = (void *)0xfffffe80,
- .mapbase = 0xfffffe80,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = SCI_IRQS,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
- {
- .port = {
- .membase = (void *)SCIF0,
- .mapbase = SCIF0,
- .iotype = UPIO_MEM,
- .irq = 55,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH3_IRDA_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)SCIF2,
- .mapbase = SCIF2,
- .iotype = UPIO_MEM,
- .irq = 59,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH3_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- }
-#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
- {
- .port = {
- .membase = (void *)0xfffffe80,
- .mapbase = 0xfffffe80,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = SCI_IRQS,
- },
- {
- .port = {
- .membase = (void *)0xa4000150,
- .mapbase = 0xa4000150,
- .iotype = UPIO_MEM,
- .irq = 59,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH3_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xa4000140,
- .mapbase = 0xa4000140,
- .iotype = UPIO_MEM,
- .irq = 55,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_IRDA,
- .irqs = SH3_IRDA_IRQS,
- .init_pins = sci_init_pins_irda,
- }
-#elif defined(CONFIG_CPU_SUBTYPE_SH7300)
- {
- .port = {
- .membase = (void *)0xA4430000,
- .mapbase = 0xA4430000,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7300_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH73180_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
- {
- .port = {
- .membase = (void *)0xffe80000,
- .mapbase = 0xffe80000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH4_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = SCI_IRQS,
- },
- {
- .port = {
- .membase = (void *)0xffe80000,
- .mapbase = 0xffe80000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH4_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
- {
- .port = {
- .membase = (void *)0xfe600000,
- .mapbase = 0xfe600000,
- .iotype = UPIO_MEM,
- .irq = 55,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7760_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xfe610000,
- .mapbase = 0xfe610000,
- .iotype = UPIO_MEM,
- .irq = 75,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH7760_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xfe620000,
- .mapbase = 0xfe620000,
- .iotype = UPIO_MEM,
- .irq = 79,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCIF,
- .irqs = SH7760_SCIF2_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 26,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = STB1_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xffe80000,
- .mapbase = 0xffe80000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH4_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
- {
- .port = {
- .iotype = UPIO_MEM,
- .irq = 42,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH5_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_H83007) || defined(CONFIG_H83068)
- {
- .port = {
- .membase = (void *)0x00ffffb0,
- .mapbase = 0x00ffffb0,
- .iotype = UPIO_MEM,
- .irq = 54,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = H8300H_SCI_IRQS0,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffffb8,
- .mapbase = 0x00ffffb8,
- .iotype = UPIO_MEM,
- .irq = 58,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCI,
- .irqs = H8300H_SCI_IRQS1,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffffc0,
- .mapbase = 0x00ffffc0,
- .iotype = UPIO_MEM,
- .irq = 62,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCI,
- .irqs = H8300H_SCI_IRQS2,
- .init_pins = sci_init_pins_sci,
- },
-#elif defined(CONFIG_H8S2678)
- {
- .port = {
- .membase = (void *)0x00ffff78,
- .mapbase = 0x00ffff78,
- .iotype = UPIO_MEM,
- .irq = 90,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = H8S_SCI_IRQS0,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffff80,
- .mapbase = 0x00ffff80,
- .iotype = UPIO_MEM,
- .irq = 94,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCI,
- .irqs = H8S_SCI_IRQS1,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffff88,
- .mapbase = 0x00ffff88,
- .iotype = UPIO_MEM,
- .irq = 98,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCI,
- .irqs = H8S_SCI_IRQS2,
- .init_pins = sci_init_pins_sci,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
- {
- .port = {
- .membase = (void *)0xff923000,
- .mapbase = 0xff923000,
- .iotype = UPIO_MEM,
- .irq = 61,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7770_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xff924000,
- .mapbase = 0xff924000,
- .iotype = UPIO_MEM,
- .irq = 62,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH7770_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xff925000,
- .mapbase = 0xff925000,
- .iotype = UPIO_MEM,
- .irq = 63,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCIF,
- .irqs = SH7770_SCIF2_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7780_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xffe10000,
- .mapbase = 0xffe10000,
- .iotype = UPIO_MEM,
- .irq = 79,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH7780_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
+static void __init sci_init_ports(void)
+{
+ static int first = 1;
+ int i;
+
+ if (!first)
+ return;
+
+ first = 0;
+
+ for (i = 0; i < SCI_NPORTS; i++) {
+ sci_ports[i].port.ops = &sci_uart_ops;
+ sci_ports[i].port.iotype = UPIO_MEM;
+ sci_ports[i].port.line = i;
+ sci_ports[i].port.fifosize = 1;
+
+#if defined(__H8300H__) || defined(__H8300S__)
+#ifdef __H8300S__
+ sci_ports[i].enable = h8300_sci_enable;
+ sci_ports[i].disable = h8300_sci_disable;
+#endif
+ sci_ports[i].port.uartclk = CONFIG_CPU_CLOCK;
+#elif defined(CONFIG_SUPERH64)
+ sci_ports[i].port.uartclk = current_cpu_data.module_clock * 16;
#else
-#error "CPU subtype not defined"
+ /*
+ * XXX: We should use a proper SCI/SCIF clock
+ */
+ {
+ struct clk *clk = clk_get("module_clk");
+ sci_ports[i].port.uartclk = clk_get_rate(clk) * 16;
+ clk_put(clk);
+ }
#endif
-};
+
+ sci_ports[i].break_timer.data = (unsigned long)&sci_ports[i];
+ sci_ports[i].break_timer.function = sci_break_timer;
+
+ init_timer(&sci_ports[i].break_timer);
+ }
+}
+
+int __init early_sci_setup(struct uart_port *port)
+{
+ if (unlikely(port->line > SCI_NPORTS))
+ return -ENODEV;
+
+ sci_init_ports();
+
+ sci_ports[port->line].port.membase = port->membase;
+ sci_ports[port->line].port.mapbase = port->mapbase;
+ sci_ports[port->line].port.type = port->type;
+
+ return 0;
+}
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
/*
@@ -1559,34 +1177,38 @@ static int __init serial_console_setup(struct console *co, char *options)
int flow = 'n';
int ret;
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index >= SCI_NPORTS)
+ co->index = 0;
+
serial_console_port = &sci_ports[co->index];
port = &serial_console_port->port;
- port->type = serial_console_port->type;
-
-#ifdef CONFIG_SUPERH64
- /* This is especially needed on sh64 to remap the SCIF */
- sci_config_port(port, 0);
-#endif
/*
- * We need to set the initial uartclk here, since otherwise it will
- * only ever be setup at sci_init() time.
+ * Also need to check port->type, we don't actually have any
+ * UPIO_PORT ports, but uart_report_port() handily misreports
+ * it anyways if we don't have a port available by the time this is
+ * called.
*/
-#if defined(__H8300H__) || defined(__H8300S__)
- port->uartclk = CONFIG_CPU_CLOCK;
+ if (!port->type)
+ return -ENODEV;
+ if (!port->membase || !port->mapbase)
+ return -ENODEV;
+
+ spin_lock_init(&port->lock);
+
+ port->type = serial_console_port->type;
+
+ if (port->flags & UPF_IOREMAP)
+ sci_config_port(port, 0);
+
+ if (serial_console_port->enable)
+ serial_console_port->enable(port);
-#if defined(__H8300S__)
- h8300_sci_enable(port, sci_enable);
-#endif
-#elif defined(CONFIG_SUPERH64)
- port->uartclk = current_cpu_data.module_clock * 16;
-#else
- {
- struct clk *clk = clk_get("module_clk");
- port->uartclk = clk_get_rate(clk) * 16;
- clk_put(clk);
- }
-#endif
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1604,17 +1226,17 @@ static struct console serial_console = {
.device = uart_console_device,
.write = serial_console_write,
.setup = serial_console_setup,
- .flags = CON_PRINTBUFFER,
+ .flags = CON_PRINTBUFFER,
.index = -1,
.data = &sci_uart_driver,
};
static int __init sci_console_init(void)
{
+ sci_init_ports();
register_console(&serial_console);
return 0;
}
-
console_initcall(sci_console_init);
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
@@ -1649,6 +1271,8 @@ int __init kgdb_console_setup(struct console *co, char *options)
int parity = 'n';
int flow = 'n';
+ spin_lock_init(&port->lock);
+
if (co->index != kgdb_portnum)
co->index = kgdb_portnum;
@@ -1677,10 +1301,10 @@ static struct console kgdb_console = {
/* Register the KGDB console so we get messages (d'oh!) */
static int __init kgdb_console_init(void)
{
+ sci_init_ports();
register_console(&kgdb_console);
return 0;
}
-
console_initcall(kgdb_console_init);
#endif /* CONFIG_SH_KGDB_CONSOLE */
@@ -1701,60 +1325,132 @@ static struct uart_driver sci_uart_driver = {
.dev_name = "ttySC",
.major = SCI_MAJOR,
.minor = SCI_MINOR_START,
+ .nr = SCI_NPORTS,
.cons = SCI_CONSOLE,
};
-static int __init sci_init(void)
+/*
+ * Register a set of serial devices attached to a platform device. The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set. Platforms that need
+ * remapping (such as sh64) should also set UPF_IOREMAP.
+ */
+static int __devinit sci_probe(struct platform_device *dev)
{
- int chan, ret;
+ struct plat_sci_port *p = dev->dev.platform_data;
+ int i;
- printk("%s", banner);
+ for (i = 0; p && p->flags != 0 && i < SCI_NPORTS; p++, i++) {
+ struct sci_port *sciport = &sci_ports[i];
- sci_uart_driver.nr = ARRAY_SIZE(sci_ports);
+ sciport->port.mapbase = p->mapbase;
- ret = uart_register_driver(&sci_uart_driver);
- if (ret == 0) {
- for (chan = 0; chan < SCI_NPORTS; chan++) {
- struct sci_port *sciport = &sci_ports[chan];
+ /*
+ * For the simple (and majority of) cases where we don't need
+ * to do any remapping, just cast the cookie directly.
+ */
+ if (p->mapbase && !p->membase && !(p->flags & UPF_IOREMAP))
+ p->membase = (void __iomem *)p->mapbase;
-#if defined(__H8300H__) || defined(__H8300S__)
- sciport->port.uartclk = CONFIG_CPU_CLOCK;
-#elif defined(CONFIG_SUPERH64)
- sciport->port.uartclk = current_cpu_data.module_clock * 16;
-#else
- struct clk *clk = clk_get("module_clk");
- sciport->port.uartclk = clk_get_rate(clk) * 16;
- clk_put(clk);
-#endif
- uart_add_one_port(&sci_uart_driver, &sciport->port);
- sciport->break_timer.data = (unsigned long)sciport;
- sciport->break_timer.function = sci_break_timer;
- init_timer(&sciport->break_timer);
- }
+ sciport->port.membase = p->membase;
+
+ sciport->port.irq = p->irqs[SCIx_TXI_IRQ];
+ sciport->port.flags = p->flags;
+ sciport->port.dev = &dev->dev;
+
+ sciport->type = sciport->port.type = p->type;
+
+ memcpy(&sciport->irqs, &p->irqs, sizeof(p->irqs));
+
+ uart_add_one_port(&sci_uart_driver, &sciport->port);
}
#ifdef CONFIG_CPU_FREQ
cpufreq_register_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
- printk("sci: CPU frequency notifier registered\n");
+ dev_info(&dev->dev, "sci: CPU frequency notifier registered\n");
#endif
#ifdef CONFIG_SH_STANDARD_BIOS
sh_bios_gdb_detach();
#endif
- return ret;
+ return 0;
}
-static void __exit sci_exit(void)
+static int __devexit sci_remove(struct platform_device *dev)
+{
+ int i;
+
+ for (i = 0; i < SCI_NPORTS; i++)
+ uart_remove_one_port(&sci_uart_driver, &sci_ports[i].port);
+
+ return 0;
+}
+
+static int sci_suspend(struct platform_device *dev, pm_message_t state)
{
- int chan;
+ int i;
+
+ for (i = 0; i < SCI_NPORTS; i++) {
+ struct sci_port *p = &sci_ports[i];
+
+ if (p->type != PORT_UNKNOWN && p->port.dev == &dev->dev)
+ uart_suspend_port(&sci_uart_driver, &p->port);
+ }
- for (chan = 0; chan < SCI_NPORTS; chan++)
- uart_remove_one_port(&sci_uart_driver, &sci_ports[chan].port);
+ return 0;
+}
+static int sci_resume(struct platform_device *dev)
+{
+ int i;
+
+ for (i = 0; i < SCI_NPORTS; i++) {
+ struct sci_port *p = &sci_ports[i];
+
+ if (p->type != PORT_UNKNOWN && p->port.dev == &dev->dev)
+ uart_resume_port(&sci_uart_driver, &p->port);
+ }
+
+ return 0;
+}
+
+static struct platform_driver sci_driver = {
+ .probe = sci_probe,
+ .remove = __devexit_p(sci_remove),
+ .suspend = sci_suspend,
+ .resume = sci_resume,
+ .driver = {
+ .name = "sh-sci",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sci_init(void)
+{
+ int ret;
+
+ printk(banner);
+
+ sci_init_ports();
+
+ ret = uart_register_driver(&sci_uart_driver);
+ if (likely(ret == 0)) {
+ ret = platform_driver_register(&sci_driver);
+ if (unlikely(ret))
+ uart_unregister_driver(&sci_uart_driver);
+ }
+
+ return ret;
+}
+
+static void __exit sci_exit(void)
+{
+ platform_driver_unregister(&sci_driver);
uart_unregister_driver(&sci_uart_driver);
}
module_init(sci_init);
module_exit(sci_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index ab320fa3237..28643c4dc85 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -10,7 +10,9 @@
* Modified to support SH7300(SH-Mobile) SCIF. Takashi Kusuda (Jun 2003).
* Modified to support H8/300 Series Yoshinori Sato (Feb 2004).
*/
+#include <linux/config.h>
#include <linux/serial_core.h>
+#include <asm/io.h>
#if defined(__H8300H__) || defined(__H8300S__)
#include <asm/gpio.h>
@@ -22,40 +24,13 @@
#endif
#endif
-/* Offsets into the sci_port->irqs array */
-#define SCIx_ERI_IRQ 0
-#define SCIx_RXI_IRQ 1
-#define SCIx_TXI_IRQ 2
-
-/* ERI, RXI, TXI, BRI */
-#define SCI_IRQS { 23, 24, 25, 0 }
-#define SH3_SCIF_IRQS { 56, 57, 59, 58 }
-#define SH3_IRDA_IRQS { 52, 53, 55, 54 }
-#define SH4_SCIF_IRQS { 40, 41, 43, 42 }
-#define STB1_SCIF1_IRQS {23, 24, 26, 25 }
-#define SH7760_SCIF0_IRQS { 52, 53, 55, 54 }
-#define SH7760_SCIF1_IRQS { 72, 73, 75, 74 }
-#define SH7760_SCIF2_IRQS { 76, 77, 79, 78 }
-#define SH7300_SCIF0_IRQS {80, 80, 80, 80 }
-#define SH73180_SCIF_IRQS {80, 81, 83, 82 }
-#define H8300H_SCI_IRQS0 {52, 53, 54, 0 }
-#define H8300H_SCI_IRQS1 {56, 57, 58, 0 }
-#define H8300H_SCI_IRQS2 {60, 61, 62, 0 }
-#define H8S_SCI_IRQS0 {88, 89, 90, 0 }
-#define H8S_SCI_IRQS1 {92, 93, 94, 0 }
-#define H8S_SCI_IRQS2 {96, 97, 98, 0 }
-#define SH5_SCIF_IRQS {39, 40, 42, 0 }
-#define SH7770_SCIF0_IRQS {61, 61, 61, 61 }
-#define SH7770_SCIF1_IRQS {62, 62, 62, 62 }
-#define SH7770_SCIF2_IRQS {63, 63, 63, 63 }
-#define SH7780_SCIF0_IRQS {40, 41, 43, 42 }
-#define SH7780_SCIF1_IRQS {76, 77, 79, 78 }
-
#if defined(CONFIG_CPU_SUBTYPE_SH7708)
# define SCSPTR 0xffffff7c /* 8 bit */
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
# define SCI_ONLY
-#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7706)
# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */
# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
@@ -99,12 +74,23 @@
# define SCPDR 0xA4050136 /* 16 bit SCIF */
# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */
# define SCIF_ONLY
+#elif defined(CONFIG_CPU_SUBTYPE_SH7710)
+# define SCSPTR0 0xA4400000 /* 16 bit SCIF */
+# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */
+# define SCIF_ONLY
#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
# define SCPDR 0xA4050138 /* 16 bit SCIF */
# define SCSPTR2 SCPDR
# define SCIF_ORER 0x0001 /* overrun error bit */
# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1 */
# define SCIF_ONLY
+#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
+# define SCSPTR0 0xffe00010 /* 16 bit SCIF */
+# define SCSPTR1 0xffe10010 /* 16 bit SCIF */
+# define SCSPTR2 0xffe20010 /* 16 bit SCIF */
+# define SCSPTR3 0xffe30010 /* 16 bit SCIF */
+# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */
+# define SCIF_ONLY
#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
# define SCIF_ORER 0x0001 /* overrun error bit */
@@ -145,7 +131,7 @@
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
# define SCSPTR1 0xffe10024 /* 16 bit SCIF */
-# define SCIF_OPER 0x0001 /* Overrun error bit */
+# define SCIF_ORER 0x0001 /* Overrun error bit */
# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
# define SCIF_ONLY
#else
@@ -273,15 +259,6 @@
*/
#define SCI_EVENT_WRITE_WAKEUP 0
-struct sci_port {
- struct uart_port port;
- int type;
- unsigned char irqs[4]; /* ERI, RXI, TXI, BRI */
- void (*init_pins)(struct uart_port *port, unsigned int cflag);
- int break_flag;
- struct timer_list break_timer;
-};
-
#define SCI_IN(size, offset) \
unsigned int addr = port->mapbase + (offset); \
if ((size) == 8) { \
@@ -336,7 +313,9 @@ struct sci_port {
}
#ifdef CONFIG_CPU_SH3
-#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+#if defined(CONFIG_CPU_SUBTYPE_SH7300) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7705) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7710)
#define SCIF_FNS(name, scif_offset, scif_size) \
CPU_SCIF_FNS(name, scif_offset, scif_size)
#else
@@ -362,7 +341,9 @@ struct sci_port {
CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size)
#endif
-#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+#if defined(CONFIG_CPU_SUBTYPE_SH7300) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7705) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7710)
SCIF_FNS(SCSMR, 0x00, 16)
SCIF_FNS(SCBRR, 0x04, 8)
SCIF_FNS(SCSCR, 0x08, 16)
@@ -447,7 +428,9 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inb(SCSPTR)&0x01 ? 1 : 0; /* SCI */
return 1;
}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7706)
static inline int sci_rxd_in(struct uart_port *port)
{
if (port->mapbase == 0xfffffe80)
@@ -467,6 +450,13 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */
return 1;
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7710)
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ if (port->mapbase == SCSPTR0)
+ return ctrl_inw(SCSPTR0 + 0x10) & 0x01 ? 1 : 0;
+ return 1;
+}
#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \
defined(CONFIG_CPU_SUBTYPE_SH7751) || \
defined(CONFIG_CPU_SUBTYPE_SH4_202)
@@ -504,6 +494,19 @@ static inline int sci_rxd_in(struct uart_port *port)
{
return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCIF0 */
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ if (port->mapbase == 0xffe00000)
+ return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xffe10000)
+ return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xffe20000)
+ return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xffe30000)
+ return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
+ return 1;
+}
#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
static inline int sci_rxd_in(struct uart_port *port)
{
@@ -587,4 +590,3 @@ static inline int sci_rxd_in(struct uart_port *port)
#else /* Generic SH */
#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1)
#endif
-
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
index d34f336d53d..0da3ebfff82 100644
--- a/drivers/serial/sunzilog.c
+++ b/drivers/serial/sunzilog.c
@@ -1270,7 +1270,7 @@ static void __init sunzilog_register_serio(struct uart_sunzilog_port *up)
}
#endif
-static void __init sunzilog_init_hw(struct uart_sunzilog_port *up)
+static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
{
struct zilog_channel __iomem *channel;
unsigned long flags;
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 00504319752..f9b1719b9a3 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -25,6 +25,7 @@ config USB_ARCH_HAS_OHCI
default y if PXA27x
default y if ARCH_EP93XX
default y if (ARCH_AT91RM9200 || ARCH_AT91SAM9261)
+ default y if ARCH_PNX4008
# PPC:
default y if STB03xxx
default y if PPC_MPC52xx
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 4710eb02ed6..97d57cfc343 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
+obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_ETRAX_USB_HOST) += host/
obj-$(CONFIG_USB_OHCI_AT91) += host/
@@ -23,6 +24,7 @@ obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
obj-$(CONFIG_USB) += storage/
+obj-$(CONFIG_USB_ACECAD) += input/
obj-$(CONFIG_USB_AIPTEK) += input/
obj-$(CONFIG_USB_ATI_REMOTE) += input/
obj-$(CONFIG_USB_HID) += input/
@@ -31,8 +33,8 @@ obj-$(CONFIG_USB_KBTAB) += input/
obj-$(CONFIG_USB_MOUSE) += input/
obj-$(CONFIG_USB_MTOUCH) += input/
obj-$(CONFIG_USB_POWERMATE) += input/
+obj-$(CONFIG_USB_TRANCEVIBRATOR)+= input/
obj-$(CONFIG_USB_WACOM) += input/
-obj-$(CONFIG_USB_ACECAD) += input/
obj-$(CONFIG_USB_XPAD) += input/
obj-$(CONFIG_USB_CATC) += net/
@@ -47,22 +49,24 @@ obj-$(CONFIG_USB_MICROTEK) += image/
obj-$(CONFIG_USB_SERIAL) += serial/
+obj-$(CONFIG_USB_ADUTUX) += misc/
+obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
obj-$(CONFIG_USB_AUERSWALD) += misc/
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
obj-$(CONFIG_USB_CYTHERM) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_EMI62) += misc/
+obj-$(CONFIG_USB_FTDI_ELAN) += misc/
obj-$(CONFIG_USB_IDMOUSE) += misc/
obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_LD) += misc/
obj-$(CONFIG_USB_LED) += misc/
obj-$(CONFIG_USB_LEGOTOWER) += misc/
+obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
+obj-$(CONFIG_USB_SISUSBVGA) += misc/
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_USS720) += misc/
-obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
-obj-$(CONFIG_USB_SISUSBVGA) += misc/
-obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index b38990adf1c..465961a26e4 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -1621,26 +1621,32 @@ static int claim_interface(struct usb_device *usb_dev,
return ret;
}
-static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static struct attribute *attrs[] = {
+ &dev_attr_stat_status.attr,
+ &dev_attr_stat_mflags.attr,
+ &dev_attr_stat_human_status.attr,
+ &dev_attr_stat_delin.attr,
+ &dev_attr_stat_vidcpe.attr,
+ &dev_attr_stat_usrate.attr,
+ &dev_attr_stat_dsrate.attr,
+ &dev_attr_stat_usattenuation.attr,
+ &dev_attr_stat_dsattenuation.attr,
+ &dev_attr_stat_usmargin.attr,
+ &dev_attr_stat_dsmargin.attr,
+ &dev_attr_stat_txflow.attr,
+ &dev_attr_stat_rxflow.attr,
+ &dev_attr_stat_uscorr.attr,
+ &dev_attr_stat_dscorr.attr,
+ &dev_attr_stat_usunc.attr,
+ &dev_attr_stat_dsunc.attr,
+};
+static struct attribute_group attr_grp = {
+ .attrs = attrs,
+};
+
+static int create_fs_entries(struct usb_interface *intf)
{
- /* sysfs interface */
- device_create_file(&intf->dev, &dev_attr_stat_status);
- device_create_file(&intf->dev, &dev_attr_stat_mflags);
- device_create_file(&intf->dev, &dev_attr_stat_human_status);
- device_create_file(&intf->dev, &dev_attr_stat_delin);
- device_create_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_create_file(&intf->dev, &dev_attr_stat_usrate);
- device_create_file(&intf->dev, &dev_attr_stat_dsrate);
- device_create_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_usmargin);
- device_create_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_create_file(&intf->dev, &dev_attr_stat_txflow);
- device_create_file(&intf->dev, &dev_attr_stat_rxflow);
- device_create_file(&intf->dev, &dev_attr_stat_uscorr);
- device_create_file(&intf->dev, &dev_attr_stat_dscorr);
- device_create_file(&intf->dev, &dev_attr_stat_usunc);
- device_create_file(&intf->dev, &dev_attr_stat_dsunc);
+ return sysfs_create_group(&intf->dev.kobj, &attr_grp);
}
static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
@@ -1708,37 +1714,25 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
return ret;
}
- create_fs_entries(sc, intf);
+ ret = create_fs_entries(intf);
+ if (ret) {
+ uea_stop(sc);
+ kfree(sc);
+ return ret;
+ }
return 0;
}
-static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static void destroy_fs_entries(struct usb_interface *intf)
{
- /* sysfs interface */
- device_remove_file(&intf->dev, &dev_attr_stat_status);
- device_remove_file(&intf->dev, &dev_attr_stat_mflags);
- device_remove_file(&intf->dev, &dev_attr_stat_human_status);
- device_remove_file(&intf->dev, &dev_attr_stat_delin);
- device_remove_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_remove_file(&intf->dev, &dev_attr_stat_usrate);
- device_remove_file(&intf->dev, &dev_attr_stat_dsrate);
- device_remove_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_usmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_txflow);
- device_remove_file(&intf->dev, &dev_attr_stat_rxflow);
- device_remove_file(&intf->dev, &dev_attr_stat_uscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_dscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_usunc);
- device_remove_file(&intf->dev, &dev_attr_stat_dsunc);
+ sysfs_remove_group(&intf->dev.kobj, &attr_grp);
}
static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
{
struct uea_softc *sc = usbatm->driver_data;
- destroy_fs_entries(sc, intf);
+ destroy_fs_entries(intf);
uea_stop(sc);
kfree(sc);
}
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 48dee4b8d8e..9cac11ca1bb 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -813,7 +813,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product)
return 0;
}
-static struct file_operations usblp_fops = {
+static const struct file_operations usblp_fops = {
.owner = THIS_MODULE,
.read = usblp_read,
.write = usblp_write,
@@ -927,7 +927,9 @@ static int usblp_probe(struct usb_interface *intf,
/* Retrieve and store the device ID string. */
usblp_cache_device_id_string(usblp);
- device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+ retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+ if (retval)
+ goto abort_intfdata;
#ifdef DEBUG
usblp_check_status(usblp, 0);
@@ -1021,18 +1023,13 @@ static int usblp_select_alts(struct usblp *usblp)
for (e = 0; e < ifd->desc.bNumEndpoints; e++) {
epd = &ifd->endpoint[e].desc;
- if ((epd->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)!=
- USB_ENDPOINT_XFER_BULK)
- continue;
-
- if (!(epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK)) {
+ if (usb_endpoint_is_bulk_out(epd))
if (!epwrite)
epwrite = epd;
- } else {
+ if (usb_endpoint_is_bulk_in(epd))
if (!epread)
epread = epd;
- }
}
/* Ignore buggy hardware without the right endpoints. */
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index ec510922af6..34e9bac319b 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -4,7 +4,7 @@
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
config.o file.o buffer.o sysfs.o endpoint.o \
- devio.o notify.o
+ devio.o notify.o generic.o
ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index f4f4ef0f377..840442a25b6 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -104,7 +104,7 @@ void *hcd_buffer_alloc (
dma_addr_t *dma
)
{
- struct usb_hcd *hcd = bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(bus);
int i;
/* some USB hosts just use PIO */
@@ -127,7 +127,7 @@ void hcd_buffer_free (
dma_addr_t dma
)
{
- struct usb_hcd *hcd = bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(bus);
int i;
if (!addr)
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 4c9e63e665b..bfb3731d42d 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -475,7 +475,9 @@ int usb_get_configuration(struct usb_device *dev)
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s\n", cfgno, "start");
- goto err;
+ dev_err(ddev, "chopping to %d config(s)\n", cfgno);
+ dev->descriptor.bNumConfigurations = cfgno;
+ break;
} else if (result < 4) {
dev_err(ddev, "config index %d descriptor too short "
"(expected %i, got %i)\n", cfgno,
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index c0f37343a27..3538c2fdadf 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -593,7 +593,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
/* Kernel lock for "lastev" protection */
static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
{
- struct usb_device_status *st = (struct usb_device_status *)file->private_data;
+ struct usb_device_status *st = file->private_data;
unsigned int mask = 0;
lock_kernel();
@@ -603,7 +603,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
unlock_kernel();
return POLLIN;
}
-
+
/* we may have dropped BKL - need to check for having lost the race */
if (file->private_data) {
kfree(st);
@@ -667,7 +667,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
return ret;
}
-struct file_operations usbfs_devices_fops = {
+const struct file_operations usbfs_devices_fops = {
.llseek = usb_device_lseek,
.read = usb_device_read,
.poll = usb_device_poll,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 218621b9958..a94c63bef63 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -59,6 +59,9 @@
#define USB_DEVICE_MAX USB_MAXBUS * 128
static struct class *usb_device_class;
+/* Mutual exclusion for removal, open, and release */
+DEFINE_MUTEX(usbfs_mutex);
+
struct async {
struct list_head asynclist;
struct dev_state *ps;
@@ -87,9 +90,10 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic");
#define MAX_USBFS_BUFFER_SIZE 16384
-static inline int connected (struct usb_device *dev)
+static inline int connected (struct dev_state *ps)
{
- return dev->state != USB_STATE_NOTATTACHED;
+ return (!list_empty(&ps->list) &&
+ ps->dev->state != USB_STATE_NOTATTACHED);
}
static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
@@ -118,7 +122,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
+ struct dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
ssize_t ret = 0;
unsigned len;
@@ -127,7 +131,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
pos = *ppos;
usb_lock_device(dev);
- if (!connected(dev)) {
+ if (!connected(ps)) {
ret = -ENODEV;
goto err;
} else if (pos < 0) {
@@ -301,7 +305,7 @@ static void snoop_urb(struct urb *urb, void __user *userurb)
static void async_completed(struct urb *urb, struct pt_regs *regs)
{
- struct async *as = (struct async *)urb->context;
+ struct async *as = urb->context;
struct dev_state *ps = as->ps;
struct siginfo sinfo;
@@ -541,25 +545,25 @@ static int usbdev_open(struct inode *inode, struct file *file)
struct dev_state *ps;
int ret;
- /*
- * no locking necessary here, as chrdev_open has the kernel lock
- * (still acquire the kernel lock for safety)
- */
+ /* Protect against simultaneous removal or release */
+ mutex_lock(&usbfs_mutex);
+
ret = -ENOMEM;
if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
- goto out_nolock;
+ goto out;
- lock_kernel();
ret = -ENOENT;
/* check if we are called from a real node or usbfs */
if (imajor(inode) == USB_DEVICE_MAJOR)
dev = usbdev_lookup_minor(iminor(inode));
if (!dev)
- dev = inode->u.generic_ip;
- if (!dev) {
- kfree(ps);
+ dev = inode->i_private;
+ if (!dev)
goto out;
- }
+ ret = usb_autoresume_device(dev, 1);
+ if (ret)
+ goto out;
+
usb_get_dev(dev);
ret = 0;
ps->dev = dev;
@@ -579,30 +583,36 @@ static int usbdev_open(struct inode *inode, struct file *file)
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
- unlock_kernel();
- out_nolock:
- return ret;
+ if (ret)
+ kfree(ps);
+ mutex_unlock(&usbfs_mutex);
+ return ret;
}
static int usbdev_release(struct inode *inode, struct file *file)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
+ struct dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
unsigned int ifnum;
usb_lock_device(dev);
+
+ /* Protect against simultaneous open */
+ mutex_lock(&usbfs_mutex);
list_del_init(&ps->list);
+ mutex_unlock(&usbfs_mutex);
+
for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
ifnum++) {
if (test_bit(ifnum, &ps->ifclaimed))
releaseintf(ps, ifnum);
}
destroy_all_async(ps);
+ usb_autosuspend_device(dev, 1);
usb_unlock_device(dev);
usb_put_dev(dev);
- ps->dev = NULL;
kfree(ps);
- return 0;
+ return 0;
}
static int proc_control(struct dev_state *ps, void __user *arg)
@@ -1322,7 +1332,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
}
}
- if (!connected(ps->dev)) {
+ if (!connected(ps)) {
kfree(buf);
return -ENODEV;
}
@@ -1349,7 +1359,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
/* let kernel drivers try to (re)bind to the interface */
case USBDEVFS_CONNECT:
usb_unlock_device(ps->dev);
- bus_rescan_devices(intf->dev.bus);
+ retval = bus_rescan_devices(intf->dev.bus);
usb_lock_device(ps->dev);
break;
@@ -1413,7 +1423,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
*/
static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
+ struct dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
void __user *p = (void __user *)arg;
int ret = -ENOTTY;
@@ -1421,7 +1431,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
usb_lock_device(dev);
- if (!connected(dev)) {
+ if (!connected(ps)) {
usb_unlock_device(dev);
return -ENODEV;
}
@@ -1556,18 +1566,18 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
/* No kernel lock - fine */
static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
- unsigned int mask = 0;
+ struct dev_state *ps = file->private_data;
+ unsigned int mask = 0;
poll_wait(file, &ps->wait, wait);
if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
mask |= POLLOUT | POLLWRNORM;
- if (!connected(ps->dev))
+ if (!connected(ps))
mask |= POLLERR | POLLHUP;
return mask;
}
-struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbfs_device_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ec890650141..113e484c763 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -17,12 +17,14 @@
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
- * generic USB things that the real drivers can use..
+ * matching, probing, releasing, suspending and resuming for
+ * real drivers.
*
*/
#include <linux/device.h>
#include <linux/usb.h>
+#include <linux/workqueue.h>
#include "hcd.h"
#include "usb.h"
@@ -34,38 +36,6 @@ struct usb_dynid {
struct usb_device_id id;
};
-
-static int generic_probe(struct device *dev)
-{
- return 0;
-}
-static int generic_remove(struct device *dev)
-{
- struct usb_device *udev = to_usb_device(dev);
-
- /* if this is only an unbind, not a physical disconnect, then
- * unconfigure the device */
- if (udev->state == USB_STATE_CONFIGURED)
- usb_set_configuration(udev, 0);
-
- /* in case the call failed or the device was suspended */
- if (udev->state >= USB_STATE_CONFIGURED)
- usb_disable_device(udev, 0);
- return 0;
-}
-
-struct device_driver usb_generic_driver = {
- .owner = THIS_MODULE,
- .name = "usb",
- .bus = &usb_bus_type,
- .probe = generic_probe,
- .remove = generic_remove,
-};
-
-/* Fun hack to determine if the struct device is a
- * usb device or a usb interface. */
-int usb_generic_driver_data;
-
#ifdef CONFIG_HOTPLUG
/*
@@ -80,6 +50,7 @@ static ssize_t store_new_id(struct device_driver *driver,
u32 idVendor = 0;
u32 idProduct = 0;
int fields = 0;
+ int retval = 0;
fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
if (fields < 2)
@@ -99,10 +70,12 @@ static ssize_t store_new_id(struct device_driver *driver,
spin_unlock(&usb_drv->dynids.lock);
if (get_driver(driver)) {
- driver_attach(driver);
+ retval = driver_attach(driver);
put_driver(driver);
}
+ if (retval)
+ return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -115,7 +88,7 @@ static int usb_create_newid_file(struct usb_driver *usb_drv)
goto exit;
if (usb_drv->probe != NULL)
- error = sysfs_create_file(&usb_drv->driver.kobj,
+ error = sysfs_create_file(&usb_drv->drvwrap.driver.kobj,
&driver_attr_new_id.attr);
exit:
return error;
@@ -127,7 +100,7 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
return;
if (usb_drv->probe != NULL)
- sysfs_remove_file(&usb_drv->driver.kobj,
+ sysfs_remove_file(&usb_drv->drvwrap.driver.kobj,
&driver_attr_new_id.attr);
}
@@ -174,21 +147,57 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in
}
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
+static int usb_probe_device(struct device *dev)
+{
+ struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+ struct usb_device *udev;
+ int error = -ENODEV;
+
+ dev_dbg(dev, "%s\n", __FUNCTION__);
+
+ if (!is_usb_device(dev)) /* Sanity check */
+ return error;
+
+ udev = to_usb_device(dev);
+
+ /* TODO: Add real matching code */
+
+ /* The device should always appear to be in use
+ * unless the driver suports autosuspend.
+ */
+ udev->pm_usage_cnt = !(udriver->supports_autosuspend);
+
+ error = udriver->probe(udev);
+ return error;
+}
+
+/* called from driver core with dev locked */
+static int usb_unbind_device(struct device *dev)
+{
+ struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+
+ udriver->disconnect(to_usb_device(dev));
+ return 0;
+}
+
+
+/* called from driver core with dev locked */
static int usb_probe_interface(struct device *dev)
{
- struct usb_interface * intf = to_usb_interface(dev);
- struct usb_driver * driver = to_usb_driver(dev->driver);
+ struct usb_driver *driver = to_usb_driver(dev->driver);
+ struct usb_interface *intf;
+ struct usb_device *udev;
const struct usb_device_id *id;
int error = -ENODEV;
dev_dbg(dev, "%s\n", __FUNCTION__);
- if (!driver->probe)
+ if (is_usb_device(dev)) /* Sanity check */
return error;
- /* FIXME we'd much prefer to just resume it ... */
- if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
- return -EHOSTUNREACH;
+
+ intf = to_usb_interface(dev);
+ udev = interface_to_usbdev(intf);
id = usb_match_id(intf, driver->id_table);
if (!id)
@@ -196,48 +205,165 @@ static int usb_probe_interface(struct device *dev)
if (id) {
dev_dbg(dev, "%s - got id\n", __FUNCTION__);
+ error = usb_autoresume_device(udev, 1);
+ if (error)
+ return error;
+
/* Interface "power state" doesn't correspond to any hardware
* state whatsoever. We use it to record when it's bound to
* a driver that may start I/0: it's not frozen/quiesced.
*/
mark_active(intf);
intf->condition = USB_INTERFACE_BINDING;
+
+ /* The interface should always appear to be in use
+ * unless the driver suports autosuspend.
+ */
+ intf->pm_usage_cnt = !(driver->supports_autosuspend);
+
error = driver->probe(intf, id);
if (error) {
mark_quiesced(intf);
+ intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND;
} else
intf->condition = USB_INTERFACE_BOUND;
+
+ usb_autosuspend_device(udev, 1);
}
return error;
}
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
static int usb_unbind_interface(struct device *dev)
{
+ struct usb_driver *driver = to_usb_driver(dev->driver);
struct usb_interface *intf = to_usb_interface(dev);
- struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+ struct usb_device *udev;
+ int error;
intf->condition = USB_INTERFACE_UNBINDING;
+ /* Autoresume for set_interface call below */
+ udev = interface_to_usbdev(intf);
+ error = usb_autoresume_device(udev, 1);
+
/* release all urbs for this interface */
usb_disable_interface(interface_to_usbdev(intf), intf);
- if (driver && driver->disconnect)
- driver->disconnect(intf);
+ driver->disconnect(intf);
/* reset other interface state */
usb_set_interface(interface_to_usbdev(intf),
intf->altsetting[0].desc.bInterfaceNumber,
0);
usb_set_intfdata(intf, NULL);
+
intf->condition = USB_INTERFACE_UNBOUND;
mark_quiesced(intf);
+ intf->needs_remote_wakeup = 0;
+
+ if (!error)
+ usb_autosuspend_device(udev, 1);
return 0;
}
+/**
+ * usb_driver_claim_interface - bind a driver to an interface
+ * @driver: the driver to be bound
+ * @iface: the interface to which it will be bound; must be in the
+ * usb device's active configuration
+ * @priv: driver data associated with that interface
+ *
+ * This is used by usb device drivers that need to claim more than one
+ * interface on a device when probing (audio and acm are current examples).
+ * No device driver should directly modify internal usb_interface or
+ * usb_device structure members.
+ *
+ * Few drivers should need to use this routine, since the most natural
+ * way to bind to an interface is to return the private data from
+ * the driver's probe() method.
+ *
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver probe() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+int usb_driver_claim_interface(struct usb_driver *driver,
+ struct usb_interface *iface, void* priv)
+{
+ struct device *dev = &iface->dev;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ int retval = 0;
+
+ if (dev->driver)
+ return -EBUSY;
+
+ dev->driver = &driver->drvwrap.driver;
+ usb_set_intfdata(iface, priv);
+
+ usb_pm_lock(udev);
+ iface->condition = USB_INTERFACE_BOUND;
+ mark_active(iface);
+ iface->pm_usage_cnt = !(driver->supports_autosuspend);
+ usb_pm_unlock(udev);
+
+ /* if interface was already added, bind now; else let
+ * the future device_add() bind it, bypassing probe()
+ */
+ if (device_is_registered(dev))
+ retval = device_bind_driver(dev);
+
+ return retval;
+}
+EXPORT_SYMBOL(usb_driver_claim_interface);
+
+/**
+ * usb_driver_release_interface - unbind a driver from an interface
+ * @driver: the driver to be unbound
+ * @iface: the interface from which it will be unbound
+ *
+ * This can be used by drivers to release an interface without waiting
+ * for their disconnect() methods to be called. In typical cases this
+ * also causes the driver disconnect() method to be called.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver disconnect() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+void usb_driver_release_interface(struct usb_driver *driver,
+ struct usb_interface *iface)
+{
+ struct device *dev = &iface->dev;
+ struct usb_device *udev = interface_to_usbdev(iface);
+
+ /* this should never happen, don't release something that's not ours */
+ if (!dev->driver || dev->driver != &driver->drvwrap.driver)
+ return;
+
+ /* don't release from within disconnect() */
+ if (iface->condition != USB_INTERFACE_BOUND)
+ return;
+
+ /* don't release if the interface hasn't been added yet */
+ if (device_is_registered(dev)) {
+ iface->condition = USB_INTERFACE_UNBINDING;
+ device_release_driver(dev);
+ }
+
+ dev->driver = NULL;
+ usb_set_intfdata(iface, NULL);
+
+ usb_pm_lock(udev);
+ iface->condition = USB_INTERFACE_UNBOUND;
+ mark_quiesced(iface);
+ iface->needs_remote_wakeup = 0;
+ usb_pm_unlock(udev);
+}
+EXPORT_SYMBOL(usb_driver_release_interface);
+
/* returns 0 if no match, 1 if match */
static int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id)
@@ -381,35 +507,223 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_match_id);
int usb_device_match(struct device *dev, struct device_driver *drv)
{
+ /* devices and interfaces are handled separately */
+ if (is_usb_device(dev)) {
+
+ /* interface drivers never match devices */
+ if (!is_usb_device_driver(drv))
+ return 0;
+
+ /* TODO: Add real matching code */
+ return 1;
+
+ } else {
+ struct usb_interface *intf;
+ struct usb_driver *usb_drv;
+ const struct usb_device_id *id;
+
+ /* device drivers never match interfaces */
+ if (is_usb_device_driver(drv))
+ return 0;
+
+ intf = to_usb_interface(dev);
+ usb_drv = to_usb_driver(drv);
+
+ id = usb_match_id(intf, usb_drv->id_table);
+ if (id)
+ return 1;
+
+ id = usb_match_dynamic_id(intf, usb_drv);
+ if (id)
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_HOTPLUG
+
+/*
+ * This sends an uevent to userspace, typically helping to load driver
+ * or other modules, configure the device, and more. Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ * We're called either from khubd (the typical case) or from root hub
+ * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
+ * delays in event delivery. Use sysfs (and DEVPATH) to make sure the
+ * device (and this configuration!) are still present.
+ */
+static int usb_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
struct usb_interface *intf;
- struct usb_driver *usb_drv;
- const struct usb_device_id *id;
+ struct usb_device *usb_dev;
+ struct usb_host_interface *alt;
+ int i = 0;
+ int length = 0;
- /* check for generic driver, which we don't match any device with */
- if (drv == &usb_generic_driver)
- return 0;
+ if (!dev)
+ return -ENODEV;
- intf = to_usb_interface(dev);
- usb_drv = to_usb_driver(drv);
+ /* driver is often null here; dev_dbg() would oops */
+ pr_debug ("usb %s: uevent\n", dev->bus_id);
- id = usb_match_id(intf, usb_drv->id_table);
- if (id)
- return 1;
+ if (is_usb_device(dev)) {
+ usb_dev = to_usb_device(dev);
+ alt = NULL;
+ } else {
+ intf = to_usb_interface(dev);
+ usb_dev = interface_to_usbdev(intf);
+ alt = intf->cur_altsetting;
+ }
+
+ if (usb_dev->devnum < 0) {
+ pr_debug ("usb %s: already deleted?\n", dev->bus_id);
+ return -ENODEV;
+ }
+ if (!usb_dev->bus) {
+ pr_debug ("usb %s: bus removed?\n", dev->bus_id);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_USB_DEVICEFS
+ /* If this is available, userspace programs can directly read
+ * all the device descriptors we don't tell them about. Or
+ * even act as usermode drivers.
+ *
+ * FIXME reduce hardwired intelligence here
+ */
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "DEVICE=/proc/bus/usb/%03d/%03d",
+ usb_dev->bus->busnum, usb_dev->devnum))
+ return -ENOMEM;
+#endif
+
+ /* per-device configurations are common */
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "PRODUCT=%x/%x/%x",
+ le16_to_cpu(usb_dev->descriptor.idVendor),
+ le16_to_cpu(usb_dev->descriptor.idProduct),
+ le16_to_cpu(usb_dev->descriptor.bcdDevice)))
+ return -ENOMEM;
+
+ /* class-based driver binding models */
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "TYPE=%d/%d/%d",
+ usb_dev->descriptor.bDeviceClass,
+ usb_dev->descriptor.bDeviceSubClass,
+ usb_dev->descriptor.bDeviceProtocol))
+ return -ENOMEM;
+
+ if (!is_usb_device(dev)) {
+
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "INTERFACE=%d/%d/%d",
+ alt->desc.bInterfaceClass,
+ alt->desc.bInterfaceSubClass,
+ alt->desc.bInterfaceProtocol))
+ return -ENOMEM;
+
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+ le16_to_cpu(usb_dev->descriptor.idVendor),
+ le16_to_cpu(usb_dev->descriptor.idProduct),
+ le16_to_cpu(usb_dev->descriptor.bcdDevice),
+ usb_dev->descriptor.bDeviceClass,
+ usb_dev->descriptor.bDeviceSubClass,
+ usb_dev->descriptor.bDeviceProtocol,
+ alt->desc.bInterfaceClass,
+ alt->desc.bInterfaceSubClass,
+ alt->desc.bInterfaceProtocol))
+ return -ENOMEM;
+ }
+
+ envp[i] = NULL;
- id = usb_match_dynamic_id(intf, usb_drv);
- if (id)
- return 1;
return 0;
}
+#else
+
+static int usb_uevent(struct device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_HOTPLUG */
+
+/**
+ * usb_register_device_driver - register a USB device (not interface) driver
+ * @new_udriver: USB operations for the device driver
+ * @owner: module owner of this driver.
+ *
+ * Registers a USB device driver with the USB core. The list of
+ * unattached devices will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized devices.
+ * Returns a negative error code on failure and 0 on success.
+ */
+int usb_register_device_driver(struct usb_device_driver *new_udriver,
+ struct module *owner)
+{
+ int retval = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ new_udriver->drvwrap.for_devices = 1;
+ new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
+ new_udriver->drvwrap.driver.bus = &usb_bus_type;
+ new_udriver->drvwrap.driver.probe = usb_probe_device;
+ new_udriver->drvwrap.driver.remove = usb_unbind_device;
+ new_udriver->drvwrap.driver.owner = owner;
+
+ retval = driver_register(&new_udriver->drvwrap.driver);
+
+ if (!retval) {
+ pr_info("%s: registered new device driver %s\n",
+ usbcore_name, new_udriver->name);
+ usbfs_update_special();
+ } else {
+ printk(KERN_ERR "%s: error %d registering device "
+ " driver %s\n",
+ usbcore_name, retval, new_udriver->name);
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(usb_register_device_driver);
+
+/**
+ * usb_deregister_device_driver - unregister a USB device (not interface) driver
+ * @udriver: USB operations of the device driver to unregister
+ * Context: must be able to sleep
+ *
+ * Unlinks the specified driver from the internal USB driver list.
+ */
+void usb_deregister_device_driver(struct usb_device_driver *udriver)
+{
+ pr_info("%s: deregistering device driver %s\n",
+ usbcore_name, udriver->name);
+
+ driver_unregister(&udriver->drvwrap.driver);
+ usbfs_update_special();
+}
+EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
+
/**
- * usb_register_driver - register a USB driver
- * @new_driver: USB operations for the driver
+ * usb_register_driver - register a USB interface driver
+ * @new_driver: USB operations for the interface driver
* @owner: module owner of this driver.
*
- * Registers a USB driver with the USB core. The list of unattached
- * interfaces will be rescanned whenever a new driver is added, allowing
- * the new driver to attach to any recognized devices.
+ * Registers a USB interface driver with the USB core. The list of
+ * unattached interfaces will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized interfaces.
* Returns a negative error code on failure and 0 on success.
*
* NOTE: if you want your driver to use the USB major number, you must call
@@ -423,23 +737,25 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
if (usb_disabled())
return -ENODEV;
- new_driver->driver.name = (char *)new_driver->name;
- new_driver->driver.bus = &usb_bus_type;
- new_driver->driver.probe = usb_probe_interface;
- new_driver->driver.remove = usb_unbind_interface;
- new_driver->driver.owner = owner;
+ new_driver->drvwrap.for_devices = 0;
+ new_driver->drvwrap.driver.name = (char *) new_driver->name;
+ new_driver->drvwrap.driver.bus = &usb_bus_type;
+ new_driver->drvwrap.driver.probe = usb_probe_interface;
+ new_driver->drvwrap.driver.remove = usb_unbind_interface;
+ new_driver->drvwrap.driver.owner = owner;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
- retval = driver_register(&new_driver->driver);
+ retval = driver_register(&new_driver->drvwrap.driver);
if (!retval) {
- pr_info("%s: registered new driver %s\n",
+ pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
usbfs_update_special();
usb_create_newid_file(new_driver);
} else {
- printk(KERN_ERR "%s: error %d registering driver %s\n",
+ printk(KERN_ERR "%s: error %d registering interface "
+ " driver %s\n",
usbcore_name, retval, new_driver->name);
}
@@ -448,8 +764,8 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
/**
- * usb_deregister - unregister a USB driver
- * @driver: USB operations of the driver to unregister
+ * usb_deregister - unregister a USB interface driver
+ * @driver: USB operations of the interface driver to unregister
* Context: must be able to sleep
*
* Unlinks the specified driver from the internal USB driver list.
@@ -460,12 +776,554 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
*/
void usb_deregister(struct usb_driver *driver)
{
- pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+ pr_info("%s: deregistering interface driver %s\n",
+ usbcore_name, driver->name);
usb_remove_newid_file(driver);
usb_free_dynids(driver);
- driver_unregister(&driver->driver);
+ driver_unregister(&driver->drvwrap.driver);
usbfs_update_special();
}
EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
+
+#ifdef CONFIG_PM
+
+/* Caller has locked udev's pm_mutex */
+static int suspend_device(struct usb_device *udev, pm_message_t msg)
+{
+ struct usb_device_driver *udriver;
+ int status = 0;
+
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state == USB_STATE_SUSPENDED)
+ goto done;
+
+ /* For devices that don't have a driver, we do a standard suspend. */
+ if (udev->dev.driver == NULL) {
+ udev->do_remote_wakeup = 0;
+ status = usb_port_suspend(udev);
+ goto done;
+ }
+
+ udriver = to_usb_device_driver(udev->dev.driver);
+ status = udriver->suspend(udev, msg);
+
+done:
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ udev->dev.power.power_state.event = msg.event;
+ return status;
+}
+
+/* Caller has locked udev's pm_mutex */
+static int resume_device(struct usb_device *udev)
+{
+ struct usb_device_driver *udriver;
+ int status = 0;
+
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state != USB_STATE_SUSPENDED)
+ goto done;
+
+ /* Can't resume it if it doesn't have a driver. */
+ if (udev->dev.driver == NULL) {
+ status = -ENOTCONN;
+ goto done;
+ }
+
+ udriver = to_usb_device_driver(udev->dev.driver);
+ status = udriver->resume(udev);
+
+done:
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ udev->dev.power.power_state.event = PM_EVENT_ON;
+ return status;
+}
+
+/* Caller has locked intf's usb_device's pm mutex */
+static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
+{
+ struct usb_driver *driver;
+ int status = 0;
+
+ /* with no hardware, USB interfaces only use FREEZE and ON states */
+ if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+ !is_active(intf))
+ goto done;
+
+ if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */
+ goto done;
+ driver = to_usb_driver(intf->dev.driver);
+
+ if (driver->suspend && driver->resume) {
+ status = driver->suspend(intf, msg);
+ if (status == 0)
+ mark_quiesced(intf);
+ else if (!interface_to_usbdev(intf)->auto_pm)
+ dev_err(&intf->dev, "%s error %d\n",
+ "suspend", status);
+ } else {
+ // FIXME else if there's no suspend method, disconnect...
+ // Not possible if auto_pm is set...
+ dev_warn(&intf->dev, "no suspend for driver %s?\n",
+ driver->name);
+ mark_quiesced(intf);
+ }
+
+done:
+ // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ intf->dev.power.power_state.event = msg.event;
+ return status;
+}
+
+/* Caller has locked intf's usb_device's pm_mutex */
+static int resume_interface(struct usb_interface *intf)
+{
+ struct usb_driver *driver;
+ int status = 0;
+
+ if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+ is_active(intf))
+ goto done;
+
+ /* Don't let autoresume interfere with unbinding */
+ if (intf->condition == USB_INTERFACE_UNBINDING)
+ goto done;
+
+ /* Can't resume it if it doesn't have a driver. */
+ if (intf->condition == USB_INTERFACE_UNBOUND) {
+ status = -ENOTCONN;
+ goto done;
+ }
+ driver = to_usb_driver(intf->dev.driver);
+
+ if (driver->resume) {
+ status = driver->resume(intf);
+ if (status)
+ dev_err(&intf->dev, "%s error %d\n",
+ "resume", status);
+ else
+ mark_active(intf);
+ } else {
+ dev_warn(&intf->dev, "no resume for driver %s?\n",
+ driver->name);
+ mark_active(intf);
+ }
+
+done:
+ // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ intf->dev.power.power_state.event = PM_EVENT_ON;
+ return status;
+}
+
+/**
+ * usb_suspend_both - suspend a USB device and its interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This is the central routine for suspending USB devices. It calls the
+ * suspend methods for all the interface drivers in @udev and then calls
+ * the suspend method for @udev itself. If an error occurs at any stage,
+ * all the interfaces which were suspended are resumed so that they remain
+ * in the same state as the device.
+ *
+ * If an autosuspend is in progress (@udev->auto_pm is set), the routine
+ * checks first to make sure that neither the device itself or any of its
+ * active interfaces is in use (pm_usage_cnt is greater than 0). If they
+ * are, the autosuspend fails.
+ *
+ * If the suspend succeeds, the routine recursively queues an autosuspend
+ * request for @udev's parent device, thereby propagating the change up
+ * the device tree. If all of the parent's children are now suspended,
+ * the parent will autosuspend in turn.
+ *
+ * The suspend method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex. Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autosuspend requests generated by a child device or
+ * interface driver may not be. Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations. However, drivers
+ * must be prepared to handle suspend calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * autosuspends) while holding @udev's device lock (preventing outside
+ * suspends).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
+{
+ int status = 0;
+ int i = 0;
+ struct usb_interface *intf;
+ struct usb_device *parent = udev->parent;
+
+ cancel_delayed_work(&udev->autosuspend);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return 0;
+ if (udev->state == USB_STATE_SUSPENDED)
+ return 0;
+
+ udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+
+ /* For autosuspend, fail fast if anything is in use.
+ * Also fail if any interfaces require remote wakeup but it
+ * isn't available. */
+ if (udev->auto_pm) {
+ if (udev->pm_usage_cnt > 0)
+ return -EBUSY;
+ if (udev->actconfig) {
+ for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ if (!is_active(intf))
+ continue;
+ if (intf->pm_usage_cnt > 0)
+ return -EBUSY;
+ if (intf->needs_remote_wakeup &&
+ !udev->do_remote_wakeup) {
+ dev_dbg(&udev->dev,
+ "remote wakeup needed for autosuspend\n");
+ return -EOPNOTSUPP;
+ }
+ }
+ i = 0;
+ }
+ }
+
+ /* Suspend all the interfaces and then udev itself */
+ if (udev->actconfig) {
+ for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ status = suspend_interface(intf, msg);
+ if (status != 0)
+ break;
+ }
+ }
+ if (status == 0)
+ status = suspend_device(udev, msg);
+
+ /* If the suspend failed, resume interfaces that did get suspended */
+ if (status != 0) {
+ while (--i >= 0) {
+ intf = udev->actconfig->interface[i];
+ resume_interface(intf);
+ }
+
+ /* If the suspend succeeded, propagate it up the tree */
+ } else if (parent)
+ usb_autosuspend_device(parent, 0);
+
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ return status;
+}
+
+/**
+ * usb_resume_both - resume a USB device and its interfaces
+ * @udev: the usb_device to resume
+ *
+ * This is the central routine for resuming USB devices. It calls the
+ * the resume method for @udev and then calls the resume methods for all
+ * the interface drivers in @udev.
+ *
+ * Before starting the resume, the routine calls itself recursively for
+ * the parent device of @udev, thereby propagating the change up the device
+ * tree and assuring that @udev will be able to resume. If the parent is
+ * unable to resume successfully, the routine fails.
+ *
+ * The resume method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex. Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autoresume requests generated by a child device or
+ * interface driver may not be. Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations. However, drivers
+ * must be prepared to handle resume calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * other autoresumes) while holding @udev's device lock (preventing outside
+ * resumes).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_resume_both(struct usb_device *udev)
+{
+ int status = 0;
+ int i;
+ struct usb_interface *intf;
+ struct usb_device *parent = udev->parent;
+
+ cancel_delayed_work(&udev->autosuspend);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+
+ /* Propagate the resume up the tree, if necessary */
+ if (udev->state == USB_STATE_SUSPENDED) {
+ if (parent) {
+ usb_pm_lock(parent);
+ parent->auto_pm = 1;
+ status = usb_resume_both(parent);
+ } else {
+
+ /* We can't progagate beyond the USB subsystem,
+ * so if a root hub's controller is suspended
+ * then we're stuck. */
+ if (udev->dev.parent->power.power_state.event !=
+ PM_EVENT_ON)
+ status = -EHOSTUNREACH;
+ }
+ if (status == 0)
+ status = resume_device(udev);
+ if (parent)
+ usb_pm_unlock(parent);
+ } else {
+
+ /* Needed only for setting udev->dev.power.power_state.event
+ * and for possible debugging message. */
+ status = resume_device(udev);
+ }
+
+ /* Now the parent won't suspend until we are finished */
+
+ if (status == 0 && udev->actconfig) {
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ resume_interface(intf);
+ }
+ }
+
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ return status;
+}
+
+#ifdef CONFIG_USB_SUSPEND
+
+/**
+ * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
+ * @udev: the usb_device to autosuspend
+ * @dec_usage_cnt: flag to decrement @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem is finished using
+ * @udev and wants to allow it to autosuspend. Examples would be when
+ * @udev's device file in usbfs is closed or after a configuration change.
+ *
+ * @dec_usage_cnt should be 1 if the subsystem previously incremented
+ * @udev's usage counter (such as by passing 1 to usb_autoresume_device);
+ * otherwise it should be 0.
+ *
+ * If the usage counter for @udev or any of its active interfaces is greater
+ * than 0, the autosuspend request will not be queued. (If an interface
+ * driver does not support autosuspend then its usage counter is permanently
+ * positive.) Likewise, if an interface driver requires remote-wakeup
+ * capability during autosuspend but remote wakeup is disabled, the
+ * autosuspend will fail.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt)
+{
+ usb_pm_lock(udev);
+ udev->pm_usage_cnt -= dec_usage_cnt;
+ if (udev->pm_usage_cnt <= 0)
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ USB_AUTOSUSPEND_DELAY);
+ usb_pm_unlock(udev);
+ // dev_dbg(&udev->dev, "%s: cnt %d\n",
+ // __FUNCTION__, udev->pm_usage_cnt);
+}
+
+/**
+ * usb_autoresume_device - immediately autoresume a USB device and its interfaces
+ * @udev: the usb_device to autoresume
+ * @inc_usage_cnt: flag to increment @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem wants to use @udev
+ * and needs to guarantee that it is not suspended. In addition, the
+ * caller can prevent @udev from being autosuspended subsequently. (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * Examples would be when @udev's device file in usbfs is opened (autosuspend
+ * should be prevented until the file is closed) or when a remote-wakeup
+ * request is received (later autosuspends should not be prevented).
+ *
+ * @inc_usage_cnt should be 1 to increment @udev's usage counter and prevent
+ * autosuspends. This prevention will persist until the usage counter is
+ * decremented again (such as by passing 1 to usb_autosuspend_device).
+ * Otherwise @inc_usage_cnt should be 0 to leave the usage counter unchanged.
+ * Regardless, if the autoresume fails then the usage counter is not
+ * incremented.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary (and attempting it might cause deadlock).
+ *
+ * This routine can run only in process context.
+ */
+int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt)
+{
+ int status;
+
+ usb_pm_lock(udev);
+ udev->pm_usage_cnt += inc_usage_cnt;
+ udev->auto_pm = 1;
+ status = usb_resume_both(udev);
+ if (status != 0)
+ udev->pm_usage_cnt -= inc_usage_cnt;
+ usb_pm_unlock(udev);
+ // dev_dbg(&udev->dev, "%s: status %d cnt %d\n",
+ // __FUNCTION__, status, udev->pm_usage_cnt);
+ return status;
+}
+
+/**
+ * usb_autopm_put_interface - decrement a USB interface's PM-usage counter
+ * @intf: the usb_interface whose counter should be decremented
+ *
+ * This routine should be called by an interface driver when it is
+ * finished using @intf and wants to allow it to autosuspend. A typical
+ * example would be a character-device driver when its device file is
+ * closed.
+ *
+ * The routine decrements @intf's usage counter. When the counter reaches
+ * 0, a delayed autosuspend request for @intf's device is queued. When
+ * the delay expires, if @intf->pm_usage_cnt is still <= 0 along with all
+ * the other usage counters for the sibling interfaces and @intf's
+ * usb_device, the device and all its interfaces will be autosuspended.
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver. The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface. The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * If the driver has set @intf->needs_remote_wakeup then autosuspend will
+ * take place only if the device's remote-wakeup facility is enabled.
+ *
+ * Suspend method calls queued by this routine can arrive at any time
+ * while @intf is resumed and its usage counter is equal to 0. They are
+ * not protected by the usb_device's lock but only by its pm_mutex.
+ * Drivers must provide their own synchronization.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autopm_put_interface(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ usb_pm_lock(udev);
+ if (intf->condition != USB_INTERFACE_UNBOUND &&
+ --intf->pm_usage_cnt <= 0) {
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ USB_AUTOSUSPEND_DELAY);
+ }
+ usb_pm_unlock(udev);
+ // dev_dbg(&intf->dev, "%s: cnt %d\n",
+ // __FUNCTION__, intf->pm_usage_cnt);
+}
+EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
+
+/**
+ * usb_autopm_get_interface - increment a USB interface's PM-usage counter
+ * @intf: the usb_interface whose counter should be incremented
+ *
+ * This routine should be called by an interface driver when it wants to
+ * use @intf and needs to guarantee that it is not suspended. In addition,
+ * the routine prevents @intf from being autosuspended subsequently. (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * This prevention will persist until usb_autopm_put_interface() is called
+ * or @intf is unbound. A typical example would be a character-device
+ * driver when its device file is opened.
+ *
+ * The routine increments @intf's usage counter. So long as the counter
+ * is greater than 0, autosuspend will not be allowed for @intf or its
+ * usb_device. When the driver is finished using @intf it should call
+ * usb_autopm_put_interface() to decrement the usage counter and queue
+ * a delayed autosuspend request (if the counter is <= 0).
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver. The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface. The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * Resume method calls generated by this routine can arrive at any time
+ * while @intf is suspended. They are not protected by the usb_device's
+ * lock but only by its pm_mutex. Drivers must provide their own
+ * synchronization.
+ *
+ * This routine can run only in process context.
+ */
+int usb_autopm_get_interface(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ int status;
+
+ usb_pm_lock(udev);
+ if (intf->condition == USB_INTERFACE_UNBOUND)
+ status = -ENODEV;
+ else {
+ ++intf->pm_usage_cnt;
+ udev->auto_pm = 1;
+ status = usb_resume_both(udev);
+ if (status != 0)
+ --intf->pm_usage_cnt;
+ }
+ usb_pm_unlock(udev);
+ // dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
+ // __FUNCTION__, status, intf->pm_usage_cnt);
+ return status;
+}
+EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
+
+#endif /* CONFIG_USB_SUSPEND */
+
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+ int status;
+
+ if (is_usb_device(dev)) {
+ struct usb_device *udev = to_usb_device(dev);
+
+ usb_pm_lock(udev);
+ udev->auto_pm = 0;
+ status = usb_suspend_both(udev, message);
+ usb_pm_unlock(udev);
+ } else
+ status = 0;
+ return status;
+}
+
+static int usb_resume(struct device *dev)
+{
+ int status;
+
+ if (is_usb_device(dev)) {
+ struct usb_device *udev = to_usb_device(dev);
+
+ usb_pm_lock(udev);
+ udev->auto_pm = 0;
+ status = usb_resume_both(udev);
+ usb_pm_unlock(udev);
+
+ /* Rebind drivers that had no suspend method? */
+ } else
+ status = 0;
+ return status;
+}
+
+#endif /* CONFIG_PM */
+
+struct bus_type usb_bus_type = {
+ .name = "usb",
+ .match = usb_device_match,
+ .uevent = usb_uevent,
+#ifdef CONFIG_PM
+ .suspend = usb_suspend,
+ .resume = usb_resume,
+#endif
+};
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 247b5a4913a..3ebb90149e9 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -207,9 +207,9 @@ static void ep_device_release(struct device *dev)
kfree(ep_dev);
}
-void usb_create_ep_files(struct device *parent,
- struct usb_host_endpoint *endpoint,
- struct usb_device *udev)
+int usb_create_ep_files(struct device *parent,
+ struct usb_host_endpoint *endpoint,
+ struct usb_device *udev)
{
char name[8];
struct ep_device *ep_dev;
@@ -242,19 +242,33 @@ void usb_create_ep_files(struct device *parent,
retval = device_register(&ep_dev->dev);
if (retval)
goto error;
- sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+ retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+ if (retval)
+ goto error_group;
endpoint->ep_dev = ep_dev;
/* create the symlink to the old-style "ep_XX" directory */
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
- sysfs_create_link(&parent->kobj, &endpoint->ep_dev->dev.kobj, name);
-
+ retval = sysfs_create_link(&parent->kobj,
+ &endpoint->ep_dev->dev.kobj, name);
+ if (retval)
+ goto error_link;
exit:
- return;
+ return retval;
+
+error_link:
+ sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+
+error_group:
+ device_unregister(&ep_dev->dev);
+ endpoint->ep_dev = NULL;
+ destroy_endpoint_class();
+ return retval;
error:
kfree(ep_dev);
- return;
+ destroy_endpoint_class();
+ return retval;
}
void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 8de4f8c99d6..c376c655c5d 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -55,7 +55,7 @@ static int usb_open(struct inode * inode, struct file * file)
return err;
}
-static struct file_operations usb_fops = {
+static const struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
};
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
new file mode 100644
index 00000000000..16332cc5794
--- /dev/null
+++ b/drivers/usb/core/generic.c
@@ -0,0 +1,208 @@
+/*
+ * drivers/usb/generic.c - generic driver for USB devices (not interfaces)
+ *
+ * (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * based on drivers/usb/usb.c which had the following copyrights:
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2004
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ * (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/usb.h>
+#include "usb.h"
+
+static inline const char *plural(int n)
+{
+ return (n == 1 ? "" : "s");
+}
+
+static int choose_configuration(struct usb_device *udev)
+{
+ int i;
+ int num_configs;
+ int insufficient_power = 0;
+ struct usb_host_config *c, *best;
+
+ best = NULL;
+ c = udev->config;
+ num_configs = udev->descriptor.bNumConfigurations;
+ for (i = 0; i < num_configs; (i++, c++)) {
+ struct usb_interface_descriptor *desc = NULL;
+
+ /* It's possible that a config has no interfaces! */
+ if (c->desc.bNumInterfaces > 0)
+ desc = &c->intf_cache[0]->altsetting->desc;
+
+ /*
+ * HP's USB bus-powered keyboard has only one configuration
+ * and it claims to be self-powered; other devices may have
+ * similar errors in their descriptors. If the next test
+ * were allowed to execute, such configurations would always
+ * be rejected and the devices would not work as expected.
+ * In the meantime, we run the risk of selecting a config
+ * that requires external power at a time when that power
+ * isn't available. It seems to be the lesser of two evils.
+ *
+ * Bugzilla #6448 reports a device that appears to crash
+ * when it receives a GET_DEVICE_STATUS request! We don't
+ * have any other way to tell whether a device is self-powered,
+ * but since we don't use that information anywhere but here,
+ * the call has been removed.
+ *
+ * Maybe the GET_DEVICE_STATUS call and the test below can
+ * be reinstated when device firmwares become more reliable.
+ * Don't hold your breath.
+ */
+#if 0
+ /* Rule out self-powered configs for a bus-powered device */
+ if (bus_powered && (c->desc.bmAttributes &
+ USB_CONFIG_ATT_SELFPOWER))
+ continue;
+#endif
+
+ /*
+ * The next test may not be as effective as it should be.
+ * Some hubs have errors in their descriptor, claiming
+ * to be self-powered when they are really bus-powered.
+ * We will overestimate the amount of current such hubs
+ * make available for each port.
+ *
+ * This is a fairly benign sort of failure. It won't
+ * cause us to reject configurations that we should have
+ * accepted.
+ */
+
+ /* Rule out configs that draw too much bus current */
+ if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+ insufficient_power++;
+ continue;
+ }
+
+ /* If the first config's first interface is COMM/2/0xff
+ * (MSFT RNDIS), rule it out unless Linux has host-side
+ * RNDIS support. */
+ if (i == 0 && desc
+ && desc->bInterfaceClass == USB_CLASS_COMM
+ && desc->bInterfaceSubClass == 2
+ && desc->bInterfaceProtocol == 0xff) {
+#ifndef CONFIG_USB_NET_RNDIS_HOST
+ continue;
+#else
+ best = c;
+#endif
+ }
+
+ /* From the remaining configs, choose the first one whose
+ * first interface is for a non-vendor-specific class.
+ * Reason: Linux is more likely to have a class driver
+ * than a vendor-specific driver. */
+ else if (udev->descriptor.bDeviceClass !=
+ USB_CLASS_VENDOR_SPEC &&
+ (!desc || desc->bInterfaceClass !=
+ USB_CLASS_VENDOR_SPEC)) {
+ best = c;
+ break;
+ }
+
+ /* If all the remaining configs are vendor-specific,
+ * choose the first one. */
+ else if (!best)
+ best = c;
+ }
+
+ if (insufficient_power > 0)
+ dev_info(&udev->dev, "rejected %d configuration%s "
+ "due to insufficient available bus power\n",
+ insufficient_power, plural(insufficient_power));
+
+ if (best) {
+ i = best->desc.bConfigurationValue;
+ dev_info(&udev->dev,
+ "configuration #%d chosen from %d choice%s\n",
+ i, num_configs, plural(num_configs));
+ } else {
+ i = -1;
+ dev_warn(&udev->dev,
+ "no configuration chosen from %d choice%s\n",
+ num_configs, plural(num_configs));
+ }
+ return i;
+}
+
+static int generic_probe(struct usb_device *udev)
+{
+ int err, c;
+
+ /* put device-specific files into sysfs */
+ usb_create_sysfs_dev_files(udev);
+
+ /* Choose and set the configuration. This registers the interfaces
+ * with the driver core and lets interface drivers bind to them.
+ */
+ c = choose_configuration(udev);
+ if (c >= 0) {
+ err = usb_set_configuration(udev, c);
+ if (err) {
+ dev_err(&udev->dev, "can't set config #%d, error %d\n",
+ c, err);
+ /* This need not be fatal. The user can try to
+ * set other configurations. */
+ }
+ }
+
+ /* USB device state == configured ... usable */
+ usb_notify_add_device(udev);
+
+ return 0;
+}
+
+static void generic_disconnect(struct usb_device *udev)
+{
+ usb_notify_remove_device(udev);
+
+ /* if this is only an unbind, not a physical disconnect, then
+ * unconfigure the device */
+ if (udev->actconfig)
+ usb_set_configuration(udev, 0);
+
+ usb_remove_sysfs_dev_files(udev);
+}
+
+#ifdef CONFIG_PM
+
+static int generic_suspend(struct usb_device *udev, pm_message_t msg)
+{
+ /* USB devices enter SUSPEND state through their hubs, but can be
+ * marked for FREEZE as soon as their children are already idled.
+ * But those semantics are useless, so we equate the two (sigh).
+ */
+ return usb_port_suspend(udev);
+}
+
+static int generic_resume(struct usb_device *udev)
+{
+ return usb_port_resume(udev);
+}
+
+#endif /* CONFIG_PM */
+
+struct usb_device_driver usb_generic_driver = {
+ .name = "usb",
+ .probe = generic_probe,
+ .disconnect = generic_disconnect,
+#ifdef CONFIG_PM
+ .suspend = generic_suspend,
+ .resume = generic_resume,
+#endif
+ .supports_autosuspend = 1,
+};
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 5078fb3375e..edf4300a3f7 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -281,7 +281,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
(void) usb_hcd_pci_resume (dev);
}
- } else {
+ } else if (hcd->state != HC_STATE_HALT) {
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state);
WARN_ON(1);
@@ -413,4 +413,20 @@ EXPORT_SYMBOL (usb_hcd_pci_resume);
#endif /* CONFIG_PM */
+/**
+ * usb_hcd_pci_shutdown - shutdown host controller
+ * @dev: USB Host Controller being shutdown
+ */
+void usb_hcd_pci_shutdown (struct pci_dev *dev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = pci_get_drvdata(dev);
+ if (!hcd)
+ return;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_pci_shutdown);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index fb4d058bbde..37f9f5e7425 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -36,6 +36,7 @@
#include <linux/mutex.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
+#include <linux/platform_device.h>
#include <linux/usb.h>
@@ -344,7 +345,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
- u8 tbuf [sizeof (struct usb_hub_descriptor)];
+ u8 tbuf [sizeof (struct usb_hub_descriptor)]
+ __attribute__((aligned(4)));
const u8 *bufp = tbuf;
int len = 0;
int patch_wakeup = 0;
@@ -632,31 +634,20 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
/*-------------------------------------------------------------------------*/
-/* Asynchronous unlinks of root-hub control URBs are legal, but they
- * don't do anything. Status URB unlinks must be made in process context
- * with interrupts enabled.
+/* Unlinks of root-hub control URBs are legal, but they don't do anything
+ * since these URBs always execute synchronously.
*/
static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
- if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
- if (in_interrupt())
- return 0; /* nothing to do */
-
- spin_lock_irq(&urb->lock); /* from usb_kill_urb */
- ++urb->reject;
- spin_unlock_irq(&urb->lock);
-
- wait_event(usb_kill_urb_queue,
- atomic_read(&urb->use_count) == 0);
+ unsigned long flags;
- spin_lock_irq(&urb->lock);
- --urb->reject;
- spin_unlock_irq(&urb->lock);
+ if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+ ; /* Do nothing */
} else { /* Status URB */
if (!hcd->uses_new_polling)
- del_timer_sync (&hcd->rh_timer);
- local_irq_disable ();
+ del_timer (&hcd->rh_timer);
+ local_irq_save (flags);
spin_lock (&hcd_root_hub_lock);
if (urb == hcd->status_urb) {
hcd->status_urb = NULL;
@@ -666,7 +657,7 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
spin_unlock (&hcd_root_hub_lock);
if (urb)
usb_hcd_giveback_urb (hcd, urb, NULL);
- local_irq_enable ();
+ local_irq_restore (flags);
}
return 0;
@@ -674,31 +665,6 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
/*-------------------------------------------------------------------------*/
-/* exported only within usbcore */
-struct usb_bus *usb_bus_get(struct usb_bus *bus)
-{
- if (bus)
- kref_get(&bus->kref);
- return bus;
-}
-
-static void usb_host_release(struct kref *kref)
-{
- struct usb_bus *bus = container_of(kref, struct usb_bus, kref);
-
- if (bus->release)
- bus->release(bus);
-}
-
-/* exported only within usbcore */
-void usb_bus_put(struct usb_bus *bus)
-{
- if (bus)
- kref_put(&bus->kref, usb_host_release);
-}
-
-/*-------------------------------------------------------------------------*/
-
static struct class *usb_host_class;
int usb_host_init(void)
@@ -730,39 +696,12 @@ static void usb_bus_init (struct usb_bus *bus)
bus->devnum_next = 1;
bus->root_hub = NULL;
- bus->hcpriv = NULL;
bus->busnum = -1;
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
bus->bandwidth_isoc_reqs = 0;
INIT_LIST_HEAD (&bus->bus_list);
-
- kref_init(&bus->kref);
-}
-
-/**
- * usb_alloc_bus - creates a new USB host controller structure
- * @op: pointer to a struct usb_operations that this bus structure should use
- * Context: !in_interrupt()
- *
- * Creates a USB host controller bus structure with the specified
- * usb_operations and initializes all the necessary internal objects.
- *
- * If no memory is available, NULL is returned.
- *
- * The caller should call usb_put_bus() when it is finished with the structure.
- */
-struct usb_bus *usb_alloc_bus (struct usb_operations *op)
-{
- struct usb_bus *bus;
-
- bus = kzalloc (sizeof *bus, GFP_KERNEL);
- if (!bus)
- return NULL;
- usb_bus_init (bus);
- bus->op = op;
- return bus;
}
/*-------------------------------------------------------------------------*/
@@ -897,8 +836,7 @@ void usb_enable_root_hub_irq (struct usb_bus *bus)
struct usb_hcd *hcd;
hcd = container_of (bus, struct usb_hcd, self);
- if (hcd->driver->hub_irq_enable && !hcd->poll_rh &&
- hcd->state != HC_STATE_HALT)
+ if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT)
hcd->driver->hub_irq_enable (hcd);
}
@@ -1112,10 +1050,10 @@ static void urb_unlink (struct urb *urb)
* expects usb_submit_urb() to have sanity checked and conditioned all
* inputs in the urb
*/
-static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
+int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
- struct usb_hcd *hcd = urb->dev->bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_host_endpoint *ep;
unsigned long flags;
@@ -1186,7 +1124,7 @@ doit:
/* lower level hcd code should use *_dma exclusively,
* unless it uses pio or talks to another transport.
*/
- if (hcd->self.controller->dma_mask) {
+ if (hcd->self.uses_dma) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
urb->setup_dma = dma_map_single (
@@ -1221,9 +1159,10 @@ done:
/*-------------------------------------------------------------------------*/
/* called in any context */
-static int hcd_get_frame_number (struct usb_device *udev)
+int usb_hcd_get_frame_number (struct usb_device *udev)
{
- struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
if (!HC_IS_RUNNING (hcd->state))
return -ESHUTDOWN;
return hcd->driver->get_frame_number (hcd);
@@ -1263,7 +1202,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
* caller guarantees urb won't be recycled till both unlink()
* and the urb's completion function return
*/
-static int hcd_unlink_urb (struct urb *urb, int status)
+int usb_hcd_unlink_urb (struct urb *urb, int status)
{
struct usb_host_endpoint *ep;
struct usb_hcd *hcd = NULL;
@@ -1296,7 +1235,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
spin_lock (&hcd_data_lock);
sys = &urb->dev->dev;
- hcd = urb->dev->bus->hcpriv;
+ hcd = bus_to_hcd(urb->dev->bus);
if (hcd == NULL) {
retval = -ENODEV;
goto done;
@@ -1354,41 +1293,33 @@ done:
/*-------------------------------------------------------------------------*/
/* disables the endpoint: cancels any pending urbs, then synchronizes with
- * the hcd to make sure all endpoint state is gone from hardware. use for
+ * the hcd to make sure all endpoint state is gone from hardware, and then
+ * waits until the endpoint's queue is completely drained. use for
* set_configuration, set_interface, driver removal, physical disconnect.
*
* example: a qh stored in ep->hcpriv, holding state related to endpoint
* type, maxpacket size, toggle, halt status, and scheduling.
*/
-static void
-hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
+void usb_hcd_endpoint_disable (struct usb_device *udev,
+ struct usb_host_endpoint *ep)
{
struct usb_hcd *hcd;
struct urb *urb;
- hcd = udev->bus->hcpriv;
+ hcd = bus_to_hcd(udev->bus);
WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
udev->state != USB_STATE_NOTATTACHED);
local_irq_disable ();
- /* FIXME move most of this into message.c as part of its
- * endpoint disable logic
- */
-
/* ep is already gone from udev->ep_{in,out}[]; no more submits */
rescan:
spin_lock (&hcd_data_lock);
list_for_each_entry (urb, &ep->urb_list, urb_list) {
int tmp;
- /* another cpu may be in hcd, spinning on hcd_data_lock
- * to giveback() this urb. the races here should be
- * small, but a full fix needs a new "can't submit"
- * urb state.
- * FIXME urb->reject should allow that...
- */
+ /* the urb may already have been unlinked */
if (urb->status != -EINPROGRESS)
continue;
usb_get_urb (urb);
@@ -1430,6 +1361,30 @@ rescan:
might_sleep ();
if (hcd->driver->endpoint_disable)
hcd->driver->endpoint_disable (hcd, ep);
+
+ /* Wait until the endpoint queue is completely empty. Most HCDs
+ * will have done this already in their endpoint_disable method,
+ * but some might not. And there could be root-hub control URBs
+ * still pending since they aren't affected by the HCDs'
+ * endpoint_disable methods.
+ */
+ while (!list_empty (&ep->urb_list)) {
+ spin_lock_irq (&hcd_data_lock);
+
+ /* The list may have changed while we acquired the spinlock */
+ urb = NULL;
+ if (!list_empty (&ep->urb_list)) {
+ urb = list_entry (ep->urb_list.prev, struct urb,
+ urb_list);
+ usb_get_urb (urb);
+ }
+ spin_unlock_irq (&hcd_data_lock);
+
+ if (urb) {
+ usb_kill_urb (urb);
+ usb_put_urb (urb);
+ }
+ }
}
/*-------------------------------------------------------------------------*/
@@ -1476,50 +1431,6 @@ int hcd_bus_resume (struct usb_bus *bus)
return status;
}
-/*
- * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports
- * @hcd: host controller for this root hub
- *
- * This call arranges that usb_hcd_resume_root_hub() is safe to call later;
- * that the HCD's root hub polling is deactivated; and that the root's hub
- * driver is suspended. HCDs may call this to autosuspend when their root
- * hub's downstream ports are all inactive: unpowered, disconnected,
- * disabled, or suspended.
- *
- * The HCD will autoresume on device connect change detection (using SRP
- * or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling
- * from any ports that are suspended (if that is enabled). In most cases,
- * overcurrent signaling (on powered ports) will also start autoresume.
- *
- * Always called with IRQs blocked.
- */
-void usb_hcd_suspend_root_hub (struct usb_hcd *hcd)
-{
- struct urb *urb;
-
- spin_lock (&hcd_root_hub_lock);
- usb_suspend_root_hub (hcd->self.root_hub);
-
- /* force status urb to complete/unlink while suspended */
- if (hcd->status_urb) {
- urb = hcd->status_urb;
- urb->status = -ECONNRESET;
- urb->hcpriv = NULL;
- urb->actual_length = 0;
-
- del_timer (&hcd->rh_timer);
- hcd->poll_pending = 0;
- hcd->status_urb = NULL;
- } else
- urb = NULL;
- spin_unlock (&hcd_root_hub_lock);
- hcd->state = HC_STATE_SUSPENDED;
-
- if (urb)
- usb_hcd_giveback_urb (hcd, urb, NULL);
-}
-EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
-
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
@@ -1583,20 +1494,6 @@ EXPORT_SYMBOL (usb_bus_start_enum);
/*-------------------------------------------------------------------------*/
-/*
- * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
- */
-static struct usb_operations usb_hcd_operations = {
- .get_frame_number = hcd_get_frame_number,
- .submit_urb = hcd_submit_urb,
- .unlink_urb = hcd_unlink_urb,
- .buffer_alloc = hcd_buffer_alloc,
- .buffer_free = hcd_buffer_free,
- .disable = hcd_endpoint_disable,
-};
-
-/*-------------------------------------------------------------------------*/
-
/**
* usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB
@@ -1617,8 +1514,9 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs
at_root_hub = (urb->dev == hcd->self.root_hub);
urb_unlink (urb);
- /* lower level hcd code should use *_dma exclusively */
- if (hcd->self.controller->dma_mask && !at_root_hub) {
+ /* lower level hcd code should use *_dma exclusively if the
+ * host controller does DMA */
+ if (hcd->self.uses_dma && !at_root_hub) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
dma_unmap_single (hcd->self.controller, urb->setup_dma,
@@ -1704,14 +1602,6 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
/*-------------------------------------------------------------------------*/
-static void hcd_release (struct usb_bus *bus)
-{
- struct usb_hcd *hcd;
-
- hcd = container_of(bus, struct usb_hcd, self);
- kfree(hcd);
-}
-
/**
* usb_create_hcd - create and initialize an HCD structure
* @driver: HC driver that will use this hcd
@@ -1736,13 +1626,12 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
return NULL;
}
dev_set_drvdata(dev, hcd);
+ kref_init(&hcd->kref);
usb_bus_init(&hcd->self);
- hcd->self.op = &usb_hcd_operations;
- hcd->self.hcpriv = hcd;
- hcd->self.release = &hcd_release;
hcd->self.controller = dev;
hcd->self.bus_name = bus_name;
+ hcd->self.uses_dma = (dev->dma_mask != NULL);
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
@@ -1756,10 +1645,25 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
}
EXPORT_SYMBOL (usb_create_hcd);
+static void hcd_release (struct kref *kref)
+{
+ struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+
+ kfree(hcd);
+}
+
+struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+{
+ if (hcd)
+ kref_get (&hcd->kref);
+ return hcd;
+}
+EXPORT_SYMBOL (usb_get_hcd);
+
void usb_put_hcd (struct usb_hcd *hcd)
{
- dev_set_drvdata(hcd->self.controller, NULL);
- usb_bus_put(&hcd->self);
+ if (hcd)
+ kref_put (&hcd->kref, hcd_release);
}
EXPORT_SYMBOL (usb_put_hcd);
@@ -1915,6 +1819,16 @@ void usb_remove_hcd(struct usb_hcd *hcd)
}
EXPORT_SYMBOL (usb_remove_hcd);
+void
+usb_hcd_platform_shutdown(struct platform_device* dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_platform_shutdown);
+
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_MON)
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 7022aafb2ae..676877c15f8 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -55,12 +55,13 @@
/*-------------------------------------------------------------------------*/
-struct usb_hcd { /* usb_bus.hcpriv points to this */
+struct usb_hcd {
/*
* housekeeping
*/
struct usb_bus self; /* hcd is-a bus */
+ struct kref kref; /* reference counter */
const char *product_desc; /* product/vendor string */
char irq_descr[24]; /* driver + bus # */
@@ -85,6 +86,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
unsigned uses_new_polling:1;
unsigned poll_rh:1; /* poll for rh status? */
unsigned poll_pending:1; /* status has changed? */
+ unsigned wireless:1; /* Wireless USB HCD */
int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
@@ -128,8 +130,10 @@ static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
return &hcd->self;
}
-
-// urb.hcpriv is really hardware-specific
+static inline struct usb_hcd *bus_to_hcd (struct usb_bus *bus)
+{
+ return container_of(bus, struct usb_hcd, self);
+}
struct hcd_timeout { /* timeouts we allocate */
struct list_head timeout_list;
@@ -138,28 +142,6 @@ struct hcd_timeout { /* timeouts we allocate */
/*-------------------------------------------------------------------------*/
-/*
- * FIXME usb_operations should vanish or become hc_driver,
- * when usb_bus and usb_hcd become the same thing.
- */
-
-struct usb_operations {
- int (*get_frame_number) (struct usb_device *usb_dev);
- int (*submit_urb) (struct urb *urb, gfp_t mem_flags);
- int (*unlink_urb) (struct urb *urb, int status);
-
- /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
- void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
- gfp_t mem_flags,
- dma_addr_t *dma);
- void (*buffer_free)(struct usb_bus *bus, size_t size,
- void *addr, dma_addr_t dma);
-
- void (*disable)(struct usb_device *udev,
- struct usb_host_endpoint *ep);
-};
-
-/* each driver provides one of these, and hardware init support */
struct pt_regs;
@@ -192,6 +174,9 @@ struct hc_driver {
/* cleanly make HCD stop writing memory and doing I/O */
void (*stop) (struct usb_hcd *hcd);
+ /* shutdown HCD */
+ void (*shutdown) (struct usb_hcd *hcd);
+
/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd);
@@ -218,15 +203,25 @@ struct hc_driver {
/* Needed only if port-change IRQs are level-triggered */
};
-extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags);
+extern int usb_hcd_unlink_urb (struct urb *urb, int status);
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb,
+ struct pt_regs *regs);
+extern void usb_hcd_endpoint_disable (struct usb_device *udev,
+ struct usb_host_endpoint *ep);
+extern int usb_hcd_get_frame_number (struct usb_device *udev);
extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, char *bus_name);
+extern struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd);
extern void usb_put_hcd (struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags);
extern void usb_remove_hcd(struct usb_hcd *hcd);
+struct platform_device;
+extern void usb_hcd_platform_shutdown(struct platform_device* dev);
+
#ifdef CONFIG_PCI
struct pci_dev;
struct pci_device_id;
@@ -239,6 +234,8 @@ extern int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t state);
extern int usb_hcd_pci_resume (struct pci_dev *dev);
#endif /* CONFIG_PM */
+extern void usb_hcd_pci_shutdown (struct pci_dev *dev);
+
#endif /* CONFIG_PCI */
/* pci-ish (pdev null is ok) buffer alloc/mapping support */
@@ -352,8 +349,6 @@ extern long usb_calc_bus_time (int speed, int is_input,
/*-------------------------------------------------------------------------*/
-extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
-
extern void usb_set_device_state(struct usb_device *udev,
enum usb_device_state new_state);
@@ -365,9 +360,6 @@ extern struct list_head usb_bus_list;
extern struct mutex usb_bus_list_lock;
extern wait_queue_head_t usb_kill_urb_queue;
-extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
-extern void usb_bus_put (struct usb_bus *bus);
-
extern void usb_enable_root_hub_irq (struct usb_bus *bus);
extern int usb_find_interface_driver (struct usb_device *dev,
@@ -376,17 +368,11 @@ extern int usb_find_interface_driver (struct usb_device *dev,
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
#ifdef CONFIG_PM
-extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd);
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
extern void usb_root_hub_lost_power (struct usb_device *rhdev);
extern int hcd_bus_suspend (struct usb_bus *bus);
extern int hcd_bus_resume (struct usb_bus *bus);
#else
-static inline void usb_hcd_suspend_root_hub(struct usb_hcd *hcd)
-{
- return;
-}
-
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
{
return;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 26c8cb5f3e6..7676690a038 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -293,7 +293,7 @@ void usb_kick_khubd(struct usb_device *hdev)
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb, struct pt_regs *regs)
{
- struct usb_hub *hub = (struct usb_hub *)urb->context;
+ struct usb_hub *hub = urb->context;
int status;
int i;
unsigned long bits;
@@ -311,7 +311,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
goto resubmit;
hub->error = urb->status;
/* FALL THROUGH */
-
+
/* let khubd handle things */
case 0: /* we got data: port status changed */
bits = 0;
@@ -452,18 +452,14 @@ static void hub_power_on(struct usb_hub *hub)
msleep(max(pgood_delay, (unsigned) 100));
}
-static inline void __hub_quiesce(struct usb_hub *hub)
+static void hub_quiesce(struct usb_hub *hub)
{
/* (nonblocking) khubd and related activity won't re-trigger */
hub->quiescing = 1;
hub->activating = 0;
hub->resume_root_hub = 0;
-}
-static void hub_quiesce(struct usb_hub *hub)
-{
/* (blocking) stop khubd and related activity */
- __hub_quiesce(hub);
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work(&hub->leds);
@@ -868,13 +864,8 @@ descriptor_error:
endpoint = &desc->endpoint[0].desc;
- /* Output endpoint? Curiouser and curiouser.. */
- if (!(endpoint->bEndpointAddress & USB_DIR_IN))
- goto descriptor_error;
-
- /* If it's not an interrupt endpoint, we'd better punt! */
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- != USB_ENDPOINT_XFER_INT)
+ /* If it's not an interrupt in endpoint, we'd better punt! */
+ if (!usb_endpoint_is_int_in(endpoint))
goto descriptor_error;
/* We found a hub */
@@ -1022,26 +1013,29 @@ void usb_set_device_state(struct usb_device *udev,
if (udev->state == USB_STATE_NOTATTACHED)
; /* do nothing */
else if (new_state != USB_STATE_NOTATTACHED) {
- udev->state = new_state;
/* root hub wakeup capabilities are managed out-of-band
* and may involve silicon errata ... ignore them here.
*/
if (udev->parent) {
- if (new_state == USB_STATE_CONFIGURED)
+ if (udev->state == USB_STATE_SUSPENDED
+ || new_state == USB_STATE_SUSPENDED)
+ ; /* No change to wakeup settings */
+ else if (new_state == USB_STATE_CONFIGURED)
device_init_wakeup(&udev->dev,
(udev->actconfig->desc.bmAttributes
& USB_CONFIG_ATT_WAKEUP));
- else if (new_state != USB_STATE_SUSPENDED)
+ else
device_init_wakeup(&udev->dev, 0);
}
+ udev->state = new_state;
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM
/**
* usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
@@ -1059,6 +1053,12 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
unsigned long flags;
dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+
+ /* Make sure no potential wakeup events get lost,
+ * by forcing the root hub to be resumed.
+ */
+ rhdev->dev.power.prev_state.event = PM_EVENT_ON;
+
spin_lock_irqsave(&device_state_lock, flags);
hub = hdev_to_hub(rhdev);
for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
@@ -1072,7 +1072,7 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
}
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
-#endif
+#endif /* CONFIG_PM */
static void choose_address(struct usb_device *udev)
{
@@ -1148,144 +1148,28 @@ void usb_disconnect(struct usb_device **pdev)
* cleaning up all state associated with the current configuration
* so that the hardware is now fully quiesced.
*/
+ dev_dbg (&udev->dev, "unregistering device\n");
usb_disable_device(udev, 0);
- usb_notify_remove_device(udev);
+ usb_unlock_device(udev);
- /* Free the device number, remove the /proc/bus/usb entry and
- * the sysfs attributes, and delete the parent's children[]
+ /* Unregister the device. The device driver is responsible
+ * for removing the device files from usbfs and sysfs and for
+ * de-configuring the device.
+ */
+ device_del(&udev->dev);
+
+ /* Free the device number and delete the parent's children[]
* (or root_hub) pointer.
*/
- dev_dbg (&udev->dev, "unregistering device\n");
release_address(udev);
- usb_remove_sysfs_dev_files(udev);
/* Avoid races with recursively_mark_NOTATTACHED() */
spin_lock_irq(&device_state_lock);
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
- usb_unlock_device(udev);
-
- device_unregister(&udev->dev);
-}
-
-static inline const char *plural(int n)
-{
- return (n == 1 ? "" : "s");
-}
-
-static int choose_configuration(struct usb_device *udev)
-{
- int i;
- int num_configs;
- int insufficient_power = 0;
- struct usb_host_config *c, *best;
-
- best = NULL;
- c = udev->config;
- num_configs = udev->descriptor.bNumConfigurations;
- for (i = 0; i < num_configs; (i++, c++)) {
- struct usb_interface_descriptor *desc = NULL;
-
- /* It's possible that a config has no interfaces! */
- if (c->desc.bNumInterfaces > 0)
- desc = &c->intf_cache[0]->altsetting->desc;
-
- /*
- * HP's USB bus-powered keyboard has only one configuration
- * and it claims to be self-powered; other devices may have
- * similar errors in their descriptors. If the next test
- * were allowed to execute, such configurations would always
- * be rejected and the devices would not work as expected.
- * In the meantime, we run the risk of selecting a config
- * that requires external power at a time when that power
- * isn't available. It seems to be the lesser of two evils.
- *
- * Bugzilla #6448 reports a device that appears to crash
- * when it receives a GET_DEVICE_STATUS request! We don't
- * have any other way to tell whether a device is self-powered,
- * but since we don't use that information anywhere but here,
- * the call has been removed.
- *
- * Maybe the GET_DEVICE_STATUS call and the test below can
- * be reinstated when device firmwares become more reliable.
- * Don't hold your breath.
- */
-#if 0
- /* Rule out self-powered configs for a bus-powered device */
- if (bus_powered && (c->desc.bmAttributes &
- USB_CONFIG_ATT_SELFPOWER))
- continue;
-#endif
-
- /*
- * The next test may not be as effective as it should be.
- * Some hubs have errors in their descriptor, claiming
- * to be self-powered when they are really bus-powered.
- * We will overestimate the amount of current such hubs
- * make available for each port.
- *
- * This is a fairly benign sort of failure. It won't
- * cause us to reject configurations that we should have
- * accepted.
- */
-
- /* Rule out configs that draw too much bus current */
- if (c->desc.bMaxPower * 2 > udev->bus_mA) {
- insufficient_power++;
- continue;
- }
-
- /* If the first config's first interface is COMM/2/0xff
- * (MSFT RNDIS), rule it out unless Linux has host-side
- * RNDIS support. */
- if (i == 0 && desc
- && desc->bInterfaceClass == USB_CLASS_COMM
- && desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff) {
-#ifndef CONFIG_USB_NET_RNDIS_HOST
- continue;
-#else
- best = c;
-#endif
- }
-
- /* From the remaining configs, choose the first one whose
- * first interface is for a non-vendor-specific class.
- * Reason: Linux is more likely to have a class driver
- * than a vendor-specific driver. */
- else if (udev->descriptor.bDeviceClass !=
- USB_CLASS_VENDOR_SPEC &&
- (!desc || desc->bInterfaceClass !=
- USB_CLASS_VENDOR_SPEC)) {
- best = c;
- break;
- }
-
- /* If all the remaining configs are vendor-specific,
- * choose the first one. */
- else if (!best)
- best = c;
- }
-
- if (insufficient_power > 0)
- dev_info(&udev->dev, "rejected %d configuration%s "
- "due to insufficient available bus power\n",
- insufficient_power, plural(insufficient_power));
-
- if (best) {
- i = best->desc.bConfigurationValue;
- dev_info(&udev->dev,
- "configuration #%d chosen from %d choice%s\n",
- i, num_configs, plural(num_configs));
- } else {
- i = -1;
- dev_warn(&udev->dev,
- "no configuration chosen from %d choice%s\n",
- num_configs, plural(num_configs));
- }
- return i;
+ put_device(&udev->dev);
}
#ifdef DEBUG
@@ -1328,7 +1212,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
int usb_new_device(struct usb_device *udev)
{
int err;
- int c;
err = usb_get_configuration(udev);
if (err < 0) {
@@ -1371,8 +1254,7 @@ int usb_new_device(struct usb_device *udev)
USB_DT_OTG, (void **) &desc) == 0) {
if (desc->bmAttributes & USB_OTG_HNP) {
unsigned port1 = udev->portnum;
- struct usb_device *root = udev->parent;
-
+
dev_info(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
@@ -1407,9 +1289,9 @@ int usb_new_device(struct usb_device *udev)
* (Includes HNP test device.)
*/
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
- static int __usb_suspend_device(struct usb_device *,
+ static int __usb_port_suspend(struct usb_device *,
int port1);
- err = __usb_suspend_device(udev, udev->bus->otg_port);
+ err = __usb_port_suspend(udev, udev->bus->otg_port);
if (err < 0)
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
}
@@ -1418,34 +1300,15 @@ int usb_new_device(struct usb_device *udev)
}
#endif
- /* put device-specific files into sysfs */
+ /* Register the device. The device driver is responsible
+ * for adding the device files to usbfs and sysfs and for
+ * configuring the device.
+ */
err = device_add (&udev->dev);
if (err) {
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
}
- usb_create_sysfs_dev_files (udev);
-
- usb_lock_device(udev);
-
- /* choose and set the configuration. that registers the interfaces
- * with the driver core, and lets usb device drivers bind to them.
- */
- c = choose_configuration(udev);
- if (c >= 0) {
- err = usb_set_configuration(udev, c);
- if (err) {
- dev_err(&udev->dev, "can't set config #%d, error %d\n",
- c, err);
- /* This need not be fatal. The user can try to
- * set other configurations. */
- }
- }
-
- /* USB device state == configured ... usable */
- usb_notify_add_device(udev);
-
- usb_unlock_device(udev);
return 0;
@@ -1472,6 +1335,18 @@ static int hub_port_status(struct usb_hub *hub, int port1,
return ret;
}
+
+/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
+static unsigned hub_is_wusb(struct usb_hub *hub)
+{
+ struct usb_hcd *hcd;
+ if (hub->hdev->parent != NULL) /* not a root hub? */
+ return 0;
+ hcd = container_of(hub->hdev->bus, struct usb_hcd, self);
+ return hcd->wireless;
+}
+
+
#define PORT_RESET_TRIES 5
#define SET_ADDRESS_TRIES 2
#define GET_DESCRIPTOR_TRIES 2
@@ -1512,7 +1387,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
/* if we`ve finished resetting, then break out of the loop */
if (!(portstatus & USB_PORT_STAT_RESET) &&
(portstatus & USB_PORT_STAT_ENABLE)) {
- if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+ if (hub_is_wusb(hub))
+ udev->speed = USB_SPEED_VARIABLE;
+ else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
@@ -1607,6 +1484,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
kick_khubd(hub);
}
+#ifdef CONFIG_PM
#ifdef CONFIG_USB_SUSPEND
@@ -1633,7 +1511,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
* NOTE: OTG devices may issue remote wakeup (or SRP) even when
* we don't explicitly enable it here.
*/
- if (device_may_wakeup(&udev->dev)) {
+ if (udev->do_remote_wakeup) {
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP, 0,
@@ -1659,7 +1537,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
USB_CTRL_SET_TIMEOUT);
} else {
/* device has up to 10 msec to fully suspend */
- dev_dbg(&udev->dev, "usb suspend\n");
+ dev_dbg(&udev->dev, "usb %ssuspend\n",
+ udev->auto_pm ? "auto-" : "");
usb_set_device_state(udev, USB_STATE_SUSPENDED);
msleep(10);
}
@@ -1684,7 +1563,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
* the root hub for their bus goes into global suspend ... so we don't
* (falsely) update the device power state to say it suspended.
*/
-static int __usb_suspend_device (struct usb_device *udev, int port1)
+static int __usb_port_suspend (struct usb_device *udev, int port1)
{
int status = 0;
@@ -1692,49 +1571,29 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
if (port1 < 0)
return port1;
- if (udev->state == USB_STATE_SUSPENDED
- || udev->state == USB_STATE_NOTATTACHED) {
- return 0;
- }
-
- /* all interfaces must already be suspended */
- if (udev->actconfig) {
- int i;
-
- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *intf;
-
- intf = udev->actconfig->interface[i];
- if (is_active(intf)) {
- dev_dbg(&intf->dev, "nyet suspended\n");
- return -EBUSY;
- }
- }
- }
-
- /* we only change a device's upstream USB link.
- * root hubs have no upstream USB link.
+ /* we change the device's upstream USB link,
+ * but root hubs have no upstream USB link.
*/
if (udev->parent)
status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
udev);
-
- if (status == 0)
- udev->dev.power.power_state = PMSG_SUSPEND;
+ else {
+ dev_dbg(&udev->dev, "usb %ssuspend\n",
+ udev->auto_pm ? "auto-" : "");
+ usb_set_device_state(udev, USB_STATE_SUSPENDED);
+ }
return status;
}
-#endif
-
/*
- * usb_suspend_device - suspend a usb device
+ * usb_port_suspend - suspend a usb device's upstream port
* @udev: device that's no longer in active use
* Context: must be able to sleep; device not locked; pm locks held
*
* Suspends a USB device that isn't in active use, conserving power.
* Devices may wake out of a suspend, if anything important happens,
* using the remote wakeup mechanism. They may also be taken out of
- * suspend by the host, using usb_resume_device(). It's also routine
+ * suspend by the host, using usb_port_resume(). It's also routine
* to disconnect devices while they are suspended.
*
* This only affects the USB hardware for a device; its interfaces
@@ -1746,17 +1605,9 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
*
* Returns 0 on success, else negative errno.
*/
-int usb_suspend_device(struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev)
{
-#ifdef CONFIG_USB_SUSPEND
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
- return __usb_suspend_device(udev, udev->portnum);
-#else
- /* NOTE: udev->state unchanged, it's not lying ... */
- udev->dev.power.power_state = PMSG_SUSPEND;
- return 0;
-#endif
+ return __usb_port_suspend(udev, udev->portnum);
}
/*
@@ -1767,7 +1618,7 @@ int usb_suspend_device(struct usb_device *udev)
* resume (by host) or remote wakeup (by device) ... now see what changed
* in the tree that's rooted at this device.
*/
-static int finish_device_resume(struct usb_device *udev)
+static int finish_port_resume(struct usb_device *udev)
{
int status;
u16 devstatus;
@@ -1783,7 +1634,6 @@ static int finish_device_resume(struct usb_device *udev)
usb_set_device_state(udev, udev->actconfig
? USB_STATE_CONFIGURED
: USB_STATE_ADDRESS);
- udev->dev.power.power_state = PMSG_ON;
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
@@ -1798,9 +1648,6 @@ static int finish_device_resume(struct usb_device *udev)
"gone after usb resume? status %d\n",
status);
else if (udev->actconfig) {
- unsigned i;
- int (*resume)(struct device *);
-
le16_to_cpus(&devstatus);
if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
&& udev->parent) {
@@ -1811,24 +1658,9 @@ static int finish_device_resume(struct usb_device *udev)
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
- if (status) {
+ if (status)
dev_dbg(&udev->dev, "disable remote "
"wakeup, status %d\n", status);
- status = 0;
- }
- }
-
- /* resume interface drivers; if this is a hub, it
- * may have a child resume event to deal with soon
- */
- resume = udev->dev.bus->resume;
- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
- struct device *dev =
- &udev->actconfig->interface[i]->dev;
-
- down(&dev->sem);
- (void) resume(dev);
- up(&dev->sem);
}
status = 0;
@@ -1839,8 +1671,6 @@ static int finish_device_resume(struct usb_device *udev)
return status;
}
-#ifdef CONFIG_USB_SUSPEND
-
static int
hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
{
@@ -1848,6 +1678,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
// dev_dbg(hub->intfdev, "resume port %d\n", port1);
+ set_bit(port1, hub->busy_bits);
+
/* see 7.1.7.7; affects power usage, but not budgeting */
status = clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_SUSPEND);
@@ -1861,7 +1693,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
/* drive resume for at least 20 msec */
if (udev)
- dev_dbg(&udev->dev, "RESUME\n");
+ dev_dbg(&udev->dev, "usb %sresume\n",
+ udev->auto_pm ? "auto-" : "");
msleep(25);
#define LIVE_FLAGS ( USB_PORT_STAT_POWER \
@@ -1891,19 +1724,21 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
/* TRSMRCY = 10 msec */
msleep(10);
if (udev)
- status = finish_device_resume(udev);
+ status = finish_port_resume(udev);
}
}
if (status < 0)
hub_port_logical_disconnect(hub, port1);
+ clear_bit(port1, hub->busy_bits);
+ if (!hub->hdev->parent && !hub->busy_bits[0])
+ usb_enable_root_hub_irq(hub->hdev->bus);
+
return status;
}
-#endif
-
/*
- * usb_resume_device - re-activate a suspended usb device
+ * usb_port_resume - re-activate a suspended usb device's upstream port
* @udev: device to re-activate
* Context: must be able to sleep; device not locked; pm locks held
*
@@ -1915,36 +1750,24 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
*
* Returns 0 on success, else negative errno.
*/
-int usb_resume_device(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev)
{
int status;
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
-
- /* selective resume of one downstream hub-to-device port */
+ /* we change the device's upstream USB link,
+ * but root hubs have no upstream USB link.
+ */
if (udev->parent) {
-#ifdef CONFIG_USB_SUSPEND
- if (udev->state == USB_STATE_SUSPENDED) {
- // NOTE swsusp may bork us, device state being wrong...
- // NOTE this fails if parent is also suspended...
- status = hub_port_resume(hdev_to_hub(udev->parent),
- udev->portnum, udev);
- } else
-#endif
- status = 0;
- } else
- status = finish_device_resume(udev);
- if (status < 0)
- dev_dbg(&udev->dev, "can't resume, status %d\n",
- status);
-
- /* rebind drivers that had no suspend() */
- if (status == 0) {
- usb_unlock_device(udev);
- bus_rescan_devices(&usb_bus_type);
- usb_lock_device(udev);
+ // NOTE this fails if parent is also suspended...
+ status = hub_port_resume(hdev_to_hub(udev->parent),
+ udev->portnum, udev);
+ } else {
+ dev_dbg(&udev->dev, "usb %sresume\n",
+ udev->auto_pm ? "auto-" : "");
+ status = finish_port_resume(udev);
}
+ if (status < 0)
+ dev_dbg(&udev->dev, "can't resume, status %d\n", status);
return status;
}
@@ -1952,23 +1775,60 @@ static int remote_wakeup(struct usb_device *udev)
{
int status = 0;
-#ifdef CONFIG_USB_SUSPEND
+ /* All this just to avoid sending a port-resume message
+ * to the parent hub! */
- /* don't repeat RESUME sequence if this device
- * was already woken up by some other task
- */
usb_lock_device(udev);
+ usb_pm_lock(udev);
if (udev->state == USB_STATE_SUSPENDED) {
- dev_dbg(&udev->dev, "RESUME (wakeup)\n");
+ dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
/* TRSMRCY = 10 msec */
msleep(10);
- status = finish_device_resume(udev);
+ status = finish_port_resume(udev);
+ if (status == 0)
+ udev->dev.power.power_state.event = PM_EVENT_ON;
}
+ usb_pm_unlock(udev);
+
+ if (status == 0)
+ usb_autoresume_device(udev, 0);
usb_unlock_device(udev);
-#endif
return status;
}
+#else /* CONFIG_USB_SUSPEND */
+
+/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
+
+int usb_port_suspend(struct usb_device *udev)
+{
+ return 0;
+}
+
+static inline int
+finish_port_resume(struct usb_device *udev)
+{
+ return 0;
+}
+
+static inline int
+hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
+{
+ return 0;
+}
+
+int usb_port_resume(struct usb_device *udev)
+{
+ return 0;
+}
+
+static inline int remote_wakeup(struct usb_device *udev)
+{
+ return 0;
+}
+
+#endif
+
static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
{
struct usb_hub *hub = usb_get_intfdata (intf);
@@ -1980,13 +1840,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
struct usb_device *udev;
udev = hdev->children [port1-1];
- if (udev && (udev->dev.power.power_state.event
- == PM_EVENT_ON
+ if (udev && msg.event == PM_EVENT_SUSPEND &&
#ifdef CONFIG_USB_SUSPEND
- || udev->state != USB_STATE_SUSPENDED
+ udev->state != USB_STATE_SUSPENDED
+#else
+ udev->dev.power.power_state.event
+ == PM_EVENT_ON
#endif
- )) {
- dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+ ) {
+ if (!hdev->auto_pm)
+ dev_dbg(&intf->dev, "port %d nyet suspended\n",
+ port1);
return -EBUSY;
}
}
@@ -2035,66 +1899,22 @@ static int hub_resume(struct usb_interface *intf)
}
}
+ /* tell khubd to look for changes on this hub */
hub_activate(hub);
-
- /* REVISIT: this recursion probably shouldn't exist. Remove
- * this code sometime, after retesting with different root and
- * external hubs.
- */
-#ifdef CONFIG_USB_SUSPEND
- {
- unsigned port1;
-
- for (port1 = 1; port1 <= hdev->maxchild; port1++) {
- struct usb_device *udev;
- u16 portstat, portchange;
-
- udev = hdev->children [port1-1];
- status = hub_port_status(hub, port1, &portstat, &portchange);
- if (status == 0) {
- if (portchange & USB_PORT_STAT_C_SUSPEND) {
- clear_port_feature(hdev, port1,
- USB_PORT_FEAT_C_SUSPEND);
- portchange &= ~USB_PORT_STAT_C_SUSPEND;
- }
-
- /* let khubd handle disconnects etc */
- if (portchange)
- continue;
- }
-
- if (!udev || status < 0)
- continue;
- usb_lock_device(udev);
- if (portstat & USB_PORT_STAT_SUSPEND)
- status = hub_port_resume(hub, port1, udev);
- else {
- status = finish_device_resume(udev);
- if (status < 0) {
- dev_dbg(&intf->dev, "resume port %d --> %d\n",
- port1, status);
- hub_port_logical_disconnect(hub, port1);
- }
- }
- usb_unlock_device(udev);
- }
- }
-#endif
return 0;
}
-void usb_suspend_root_hub(struct usb_device *hdev)
-{
- struct usb_hub *hub = hdev_to_hub(hdev);
+#else /* CONFIG_PM */
- /* This also makes any led blinker stop retriggering. We're called
- * from irq, so the blinker might still be scheduled. Caller promises
- * that the root hub status URB will be canceled.
- */
- __hub_quiesce(hub);
- mark_quiesced(to_usb_interface(hub->intfdev));
+static inline int remote_wakeup(struct usb_device *udev)
+{
+ return 0;
}
+#define hub_suspend NULL
+#define hub_resume NULL
+#endif
+
void usb_resume_root_hub(struct usb_device *hdev)
{
struct usb_hub *hub = hdev_to_hub(hdev);
@@ -2214,6 +2034,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
+ char *speed, *type;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
@@ -2246,8 +2067,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
* it's fixed size except for full speed devices.
+ * For Wireless USB devices, ep0 max packet is always 512 (tho
+ * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
*/
switch (udev->speed) {
+ case USB_SPEED_VARIABLE: /* fixed at 512 */
+ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
+ break;
case USB_SPEED_HIGH: /* fixed at 64 */
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
break;
@@ -2265,17 +2091,21 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
+ type = "";
+ switch (udev->speed) {
+ case USB_SPEED_LOW: speed = "low"; break;
+ case USB_SPEED_FULL: speed = "full"; break;
+ case USB_SPEED_HIGH: speed = "high"; break;
+ case USB_SPEED_VARIABLE:
+ speed = "variable";
+ type = "Wireless ";
+ break;
+ default: speed = "?"; break;
+ }
dev_info (&udev->dev,
- "%s %s speed USB device using %s and address %d\n",
- (udev->config) ? "reset" : "new",
- ({ char *speed; switch (udev->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- default: speed = "?"; break;
- }; speed;}),
- udev->bus->controller->driver->name,
- udev->devnum);
+ "%s %s speed %sUSB device using %s and address %d\n",
+ (udev->config) ? "reset" : "new", speed, type,
+ udev->bus->controller->driver->name, udev->devnum);
/* Set up TT records, if needed */
if (hdev->tt) {
@@ -2317,6 +2147,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* down tremendously by NAKing the unexpectedly
* early status stage. Also, retry on all errors;
* some devices are flakey.
+ * 255 is for WUSB devices, we actually need to use 512.
+ * WUSB1.0[4.8.1].
*/
for (j = 0; j < 3; ++j) {
buf->bMaxPacketSize0 = 0;
@@ -2326,7 +2158,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
buf, GET_DESCRIPTOR_BUFSIZE,
(i ? USB_CTRL_GET_TIMEOUT : 1000));
switch (buf->bMaxPacketSize0) {
- case 8: case 16: case 32: case 64:
+ case 8: case 16: case 32: case 64: case 255:
if (buf->bDescriptorType ==
USB_DT_DEVICE) {
r = 0;
@@ -2400,7 +2232,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (retval)
goto fail;
- i = udev->descriptor.bMaxPacketSize0;
+ i = udev->descriptor.bMaxPacketSize0 == 0xff?
+ 512 : udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed != USB_SPEED_FULL ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
@@ -2585,6 +2418,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_set_device_state(udev, USB_STATE_POWERED);
udev->speed = USB_SPEED_UNKNOWN;
udev->bus_mA = hub->mA_per_port;
+ udev->level = hdev->level + 1;
/* set the address */
choose_address(udev);
@@ -2736,17 +2570,6 @@ static void hub_events(void)
usb_get_intf(intf);
spin_unlock_irq(&hub_event_lock);
- /* Is this is a root hub wanting to reactivate the downstream
- * ports? If so, be sure the interface resumes even if its
- * stub "device" node was never suspended.
- */
- if (i) {
- dpm_runtime_resume(&hdev->dev);
- dpm_runtime_resume(&intf->dev);
- usb_put_intf(intf);
- continue;
- }
-
/* Lock the device, then check to see if we were
* disconnected while waiting for the lock to succeed. */
if (locktree(hdev) < 0) {
@@ -2763,6 +2586,13 @@ static void hub_events(void)
goto loop;
}
+ /* Is this is a root hub wanting to reactivate the downstream
+ * ports? If so, be sure the interface resumes even if its
+ * stub "device" node was never suspended.
+ */
+ if (i)
+ usb_autoresume_device(hdev, 0);
+
/* If this is an inactive or suspended hub, do nothing */
if (hub->quiescing)
goto loop;
@@ -2900,7 +2730,7 @@ static void hub_events(void)
/* If this is a root hub, tell the HCD it's okay to
* re-enable port-change interrupts now. */
- if (!hdev->parent)
+ if (!hdev->parent && !hub->busy_bits[0])
usb_enable_root_hub_irq(hdev->bus);
loop:
@@ -3075,6 +2905,9 @@ int usb_reset_device(struct usb_device *udev)
break;
}
clear_bit(port1, parent_hub->busy_bits);
+ if (!parent_hdev->parent && !parent_hub->busy_bits[0])
+ usb_enable_root_hub_irq(parent_hdev->bus);
+
if (ret < 0)
goto re_enumerate;
@@ -3128,6 +2961,7 @@ re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}
+EXPORT_SYMBOL(usb_reset_device);
/**
* usb_reset_composite_device - warn interface drivers and perform a USB port reset
@@ -3163,6 +2997,9 @@ int usb_reset_composite_device(struct usb_device *udev,
return -EINVAL;
}
+ /* Prevent autosuspend during the reset */
+ usb_autoresume_device(udev, 1);
+
if (iface && iface->condition != USB_INTERFACE_BINDING)
iface = NULL;
@@ -3204,5 +3041,7 @@ int usb_reset_composite_device(struct usb_device *udev,
}
}
+ usb_autosuspend_device(udev, 1);
return ret;
}
+EXPORT_SYMBOL(usb_reset_composite_device);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 29d5f45a845..0f8e82a4d48 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -212,7 +212,8 @@ struct usb_hub {
unsigned long event_bits[1]; /* status change bitmask */
unsigned long change_bits[1]; /* ports with logical connect
status change */
- unsigned long busy_bits[1]; /* ports being reset */
+ unsigned long busy_bits[1]; /* ports being reset or
+ resumed */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 3182c2224ba..df3d152f049 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -44,7 +44,7 @@
#include "hcd.h"
static struct super_operations usbfs_ops;
-static struct file_operations default_file_operations;
+static const struct file_operations default_file_operations;
static struct vfsmount *usbfs_mount;
static int usbfs_mount_count; /* = 0 */
static int ignore_mount = 0;
@@ -249,7 +249,6 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de
inode->i_mode = mode;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
@@ -402,13 +401,13 @@ static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
static int default_open (struct inode *inode, struct file *file)
{
- if (inode->u.generic_ip)
- file->private_data = inode->u.generic_ip;
+ if (inode->i_private)
+ file->private_data = inode->i_private;
return 0;
}
-static struct file_operations default_file_operations = {
+static const struct file_operations default_file_operations = {
.read = default_read_file,
.write = default_write_file,
.open = default_open,
@@ -495,7 +494,7 @@ static int fs_create_by_name (const char *name, mode_t mode,
static struct dentry *fs_create_file (const char *name, mode_t mode,
struct dentry *parent, void *data,
- struct file_operations *fops,
+ const struct file_operations *fops,
uid_t uid, gid_t gid)
{
struct dentry *dentry;
@@ -509,7 +508,7 @@ static struct dentry *fs_create_file (const char *name, mode_t mode,
} else {
if (dentry->d_inode) {
if (data)
- dentry->d_inode->u.generic_ip = data;
+ dentry->d_inode->i_private = data;
if (fops)
dentry->d_inode->i_fop = fops;
dentry->d_inode->i_uid = uid;
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 4cc8d3e67db..85b1cd18336 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -23,59 +23,44 @@ static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
}
-static void timeout_kill(unsigned long data)
-{
- struct urb *urb = (struct urb *) data;
-
- usb_unlink_urb(urb);
-}
-
-// Starts urb and waits for completion or timeout
-// note that this call is NOT interruptible, while
-// many device driver i/o requests should be interruptible
-static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+/*
+ * Starts urb and waits for completion or timeout. Note that this call
+ * is NOT interruptible. Many device driver i/o requests should be
+ * interruptible and therefore these drivers should implement their
+ * own interruptible routines.
+ */
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
{
- struct completion done;
- struct timer_list timer;
- int status;
+ struct completion done;
+ unsigned long expire;
+ int status;
init_completion(&done);
urb->context = &done;
urb->actual_length = 0;
status = usb_submit_urb(urb, GFP_NOIO);
-
- if (status == 0) {
- if (timeout > 0) {
- init_timer(&timer);
- timer.expires = jiffies + msecs_to_jiffies(timeout);
- timer.data = (unsigned long)urb;
- timer.function = timeout_kill;
- /* grr. timeout _should_ include submit delays. */
- add_timer(&timer);
- }
- wait_for_completion(&done);
+ if (unlikely(status))
+ goto out;
+
+ expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+ if (!wait_for_completion_timeout(&done, expire)) {
+
+ dev_dbg(&urb->dev->dev,
+ "%s timed out on ep%d%s len=%d/%d\n",
+ current->comm,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+
+ usb_kill_urb(urb);
+ status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
+ } else
status = urb->status;
- /* note: HCDs return ETIMEDOUT for other reasons too */
- if (status == -ECONNRESET) {
- dev_dbg(&urb->dev->dev,
- "%s timed out on ep%d%s len=%d/%d\n",
- current->comm,
- usb_pipeendpoint(urb->pipe),
- usb_pipein(urb->pipe) ? "in" : "out",
- urb->actual_length,
- urb->transfer_buffer_length
- );
- if (urb->actual_length > 0)
- status = 0;
- else
- status = -ETIMEDOUT;
- }
- if (timeout > 0)
- del_timer_sync(&timer);
- }
-
+out:
if (actual_length)
*actual_length = urb->actual_length;
+
usb_free_urb(urb);
return status;
}
@@ -263,7 +248,7 @@ static void sg_clean (struct usb_sg_request *io)
static void sg_complete (struct urb *urb, struct pt_regs *regs)
{
- struct usb_sg_request *io = (struct usb_sg_request *) urb->context;
+ struct usb_sg_request *io = urb->context;
spin_lock (&io->lock);
@@ -999,8 +984,8 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
ep = dev->ep_in[epnum];
dev->ep_in[epnum] = NULL;
}
- if (ep && dev->bus && dev->bus->op && dev->bus->op->disable)
- dev->bus->op->disable(dev, ep);
+ if (ep && dev->bus)
+ usb_hcd_endpoint_disable(dev, ep);
}
/**
@@ -1381,9 +1366,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n");
- if (dev->state == USB_STATE_SUSPENDED)
- return -EHOSTUNREACH;
-
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
@@ -1418,6 +1400,11 @@ free_interfaces:
configuration, -i);
}
+ /* Wake up the device so we can send it the Set-Config request */
+ ret = usb_autoresume_device(dev, 1);
+ if (ret)
+ goto free_interfaces;
+
/* if it's already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers.
*/
@@ -1437,6 +1424,7 @@ free_interfaces:
dev->actconfig = cp;
if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
+ usb_autosuspend_device(dev, 1);
goto free_interfaces;
}
usb_set_device_state(dev, USB_STATE_CONFIGURED);
@@ -1505,8 +1493,68 @@ free_interfaces:
usb_create_sysfs_intf_files (intf);
}
+ usb_autosuspend_device(dev, 1);
+ return 0;
+}
+
+struct set_config_request {
+ struct usb_device *udev;
+ int config;
+ struct work_struct work;
+};
+
+/* Worker routine for usb_driver_set_configuration() */
+static void driver_set_config_work(void *_req)
+{
+ struct set_config_request *req = _req;
+
+ usb_lock_device(req->udev);
+ usb_set_configuration(req->udev, req->config);
+ usb_unlock_device(req->udev);
+ usb_put_dev(req->udev);
+ kfree(req);
+}
+
+/**
+ * usb_driver_set_configuration - Provide a way for drivers to change device configurations
+ * @udev: the device whose configuration is being updated
+ * @config: the configuration being chosen.
+ * Context: In process context, must be able to sleep
+ *
+ * Device interface drivers are not allowed to change device configurations.
+ * This is because changing configurations will destroy the interface the
+ * driver is bound to and create new ones; it would be like a floppy-disk
+ * driver telling the computer to replace the floppy-disk drive with a
+ * tape drive!
+ *
+ * Still, in certain specialized circumstances the need may arise. This
+ * routine gets around the normal restrictions by using a work thread to
+ * submit the change-config request.
+ *
+ * Returns 0 if the request was succesfully queued, error code otherwise.
+ * The caller has no way to know whether the queued request will eventually
+ * succeed.
+ */
+int usb_driver_set_configuration(struct usb_device *udev, int config)
+{
+ struct set_config_request *req;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+ req->udev = udev;
+ req->config = config;
+ INIT_WORK(&req->work, driver_set_config_work, req);
+
+ usb_get_dev(udev);
+ if (!schedule_work(&req->work)) {
+ usb_put_dev(udev);
+ kfree(req);
+ return -EINVAL;
+ }
return 0;
}
+EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
// synchronous request completion model
EXPORT_SYMBOL(usb_control_msg);
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index b042676af0a..6b36897ca15 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -50,8 +50,11 @@ void usb_notify_add_device(struct usb_device *udev)
void usb_notify_remove_device(struct usb_device *udev)
{
+ /* Protect against simultaneous usbfs open */
+ mutex_lock(&usbfs_mutex);
blocking_notifier_call_chain(&usb_notifier_list,
USB_DEVICE_REMOVE, udev);
+ mutex_unlock(&usbfs_mutex);
}
void usb_notify_add_bus(struct usb_bus *ubus)
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index dec973affb0..55d8f575206 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -60,7 +60,7 @@ static ssize_t
set_bConfigurationValue (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct usb_device *udev = udev = to_usb_device (dev);
+ struct usb_device *udev = to_usb_device (dev);
int config, value;
if (sscanf (buf, "%u", &config) != 1 || config > 255)
@@ -186,6 +186,7 @@ usb_descriptor_attr (bMaxPacketSize0, "%d\n")
static struct attribute *dev_attrs[] = {
/* current configuration's attributes */
+ &dev_attr_configuration.attr,
&dev_attr_bNumInterfaces.attr,
&dev_attr_bConfigurationValue.attr,
&dev_attr_bmAttributes.attr,
@@ -209,20 +210,40 @@ static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
-void usb_create_sysfs_dev_files (struct usb_device *udev)
+int usb_create_sysfs_dev_files(struct usb_device *udev)
{
struct device *dev = &udev->dev;
+ int retval;
- sysfs_create_group(&dev->kobj, &dev_attr_grp);
+ retval = sysfs_create_group(&dev->kobj, &dev_attr_grp);
+ if (retval)
+ return retval;
- if (udev->manufacturer)
- device_create_file (dev, &dev_attr_manufacturer);
- if (udev->product)
- device_create_file (dev, &dev_attr_product);
- if (udev->serial)
- device_create_file (dev, &dev_attr_serial);
- device_create_file (dev, &dev_attr_configuration);
- usb_create_ep_files(dev, &udev->ep0, udev);
+ if (udev->manufacturer) {
+ retval = device_create_file (dev, &dev_attr_manufacturer);
+ if (retval)
+ goto error;
+ }
+ if (udev->product) {
+ retval = device_create_file (dev, &dev_attr_product);
+ if (retval)
+ goto error;
+ }
+ if (udev->serial) {
+ retval = device_create_file (dev, &dev_attr_serial);
+ if (retval)
+ goto error;
+ }
+ retval = usb_create_ep_files(dev, &udev->ep0, udev);
+ if (retval)
+ goto error;
+ return 0;
+error:
+ usb_remove_ep_files(&udev->ep0);
+ device_remove_file(dev, &dev_attr_manufacturer);
+ device_remove_file(dev, &dev_attr_product);
+ device_remove_file(dev, &dev_attr_serial);
+ return retval;
}
void usb_remove_sysfs_dev_files (struct usb_device *udev)
@@ -238,7 +259,6 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
device_remove_file(dev, &dev_attr_product);
if (udev->serial)
device_remove_file(dev, &dev_attr_serial);
- device_remove_file (dev, &dev_attr_configuration);
}
/* Interface fields */
@@ -340,18 +360,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
usb_remove_ep_files(&iface_desc->endpoint[i]);
}
-void usb_create_sysfs_intf_files (struct usb_interface *intf)
+int usb_create_sysfs_intf_files(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_interface *alt = intf->cur_altsetting;
+ int retval;
- sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+ retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+ if (retval)
+ goto error;
if (alt->string == NULL)
alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string)
- device_create_file(&intf->dev, &dev_attr_interface);
+ retval = device_create_file(&intf->dev, &dev_attr_interface);
usb_create_intf_ep_files(intf, udev);
+ return 0;
+error:
+ if (alt->string)
+ device_remove_file(&intf->dev, &dev_attr_interface);
+ sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+ usb_remove_intf_ep_files(intf);
+ return retval;
}
void usb_remove_sysfs_intf_files (struct usb_interface *intf)
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 9864988377c..9801d08edac 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -57,7 +57,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
- urb = (struct urb *)kmalloc(sizeof(struct urb) +
+ urb = kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
if (!urb) {
@@ -221,7 +221,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
int pipe, temp, max;
struct usb_device *dev;
- struct usb_operations *op;
int is_out;
if (!urb || urb->hcpriv || !urb->complete)
@@ -233,8 +232,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
|| dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
- if (!(op = dev->bus->op) || !op->submit_urb)
- return -ENODEV;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
@@ -376,7 +373,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
urb->interval = temp;
}
- return op->submit_urb (urb, mem_flags);
+ return usb_hcd_submit_urb (urb, mem_flags);
}
/*-------------------------------------------------------------------*/
@@ -440,9 +437,9 @@ int usb_unlink_urb(struct urb *urb)
{
if (!urb)
return -EINVAL;
- if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
+ if (!(urb->dev && urb->dev->bus))
return -ENODEV;
- return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
+ return usb_hcd_unlink_urb(urb, -ECONNRESET);
}
/**
@@ -468,13 +465,13 @@ int usb_unlink_urb(struct urb *urb)
void usb_kill_urb(struct urb *urb)
{
might_sleep();
- if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
+ if (!(urb && urb->dev && urb->dev->bus))
return;
spin_lock_irq(&urb->lock);
++urb->reject;
spin_unlock_irq(&urb->lock);
- urb->dev->bus->op->unlink_urb(urb, -ENOENT);
+ usb_hcd_unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
spin_lock_irq(&urb->lock);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 184c24660a4..e4df9edf1bc 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -33,6 +33,7 @@
#include <linux/smp_lock.h>
#include <linux/usb.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
@@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore";
static int nousb; /* Disable USB when built into kernel image */
+struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */
+
/**
* usb_ifnum_to_if - get the interface object with a given interface number
@@ -67,7 +70,8 @@ static int nousb; /* Disable USB when built into kernel image */
* Don't call this function unless you are bound to one of the interfaces
* on this device or you have locked the device!
*/
-struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
+ unsigned ifnum)
{
struct usb_host_config *config = dev->actconfig;
int i;
@@ -100,8 +104,8 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
* Don't call this function unless you are bound to the intf interface
* or you have locked the device!
*/
-struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
- unsigned int altnum)
+struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf,
+ unsigned int altnum)
{
int i;
@@ -112,87 +116,6 @@ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
return NULL;
}
-/**
- * usb_driver_claim_interface - bind a driver to an interface
- * @driver: the driver to be bound
- * @iface: the interface to which it will be bound; must be in the
- * usb device's active configuration
- * @priv: driver data associated with that interface
- *
- * This is used by usb device drivers that need to claim more than one
- * interface on a device when probing (audio and acm are current examples).
- * No device driver should directly modify internal usb_interface or
- * usb_device structure members.
- *
- * Few drivers should need to use this routine, since the most natural
- * way to bind to an interface is to return the private data from
- * the driver's probe() method.
- *
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock. So driver probe() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-int usb_driver_claim_interface(struct usb_driver *driver,
- struct usb_interface *iface, void* priv)
-{
- struct device *dev = &iface->dev;
-
- if (dev->driver)
- return -EBUSY;
-
- dev->driver = &driver->driver;
- usb_set_intfdata(iface, priv);
- iface->condition = USB_INTERFACE_BOUND;
- mark_active(iface);
-
- /* if interface was already added, bind now; else let
- * the future device_add() bind it, bypassing probe()
- */
- if (device_is_registered(dev))
- device_bind_driver(dev);
-
- return 0;
-}
-
-/**
- * usb_driver_release_interface - unbind a driver from an interface
- * @driver: the driver to be unbound
- * @iface: the interface from which it will be unbound
- *
- * This can be used by drivers to release an interface without waiting
- * for their disconnect() methods to be called. In typical cases this
- * also causes the driver disconnect() method to be called.
- *
- * This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock. So driver disconnect() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-void usb_driver_release_interface(struct usb_driver *driver,
- struct usb_interface *iface)
-{
- struct device *dev = &iface->dev;
-
- /* this should never happen, don't release something that's not ours */
- if (!dev->driver || dev->driver != &driver->driver)
- return;
-
- /* don't release from within disconnect() */
- if (iface->condition != USB_INTERFACE_BOUND)
- return;
-
- /* don't release if the interface hasn't been added yet */
- if (device_is_registered(dev)) {
- iface->condition = USB_INTERFACE_UNBINDING;
- device_release_driver(dev);
- }
-
- dev->driver = NULL;
- usb_set_intfdata(iface, NULL);
- iface->condition = USB_INTERFACE_UNBOUND;
- mark_quiesced(iface);
-}
-
struct find_interface_arg {
int minor;
struct usb_interface *interface;
@@ -204,7 +127,7 @@ static int __find_interface(struct device * dev, void * data)
struct usb_interface *intf;
/* can't look at usb devices, only interfaces */
- if (dev->driver == &usb_generic_driver)
+ if (is_usb_device(dev))
return 0;
intf = to_usb_interface(dev);
@@ -227,147 +150,82 @@ static int __find_interface(struct device * dev, void * data)
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
{
struct find_interface_arg argb;
+ int retval;
argb.minor = minor;
argb.interface = NULL;
- driver_for_each_device(&drv->driver, NULL, &argb, __find_interface);
+ /* eat the error, it will be in argb.interface */
+ retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb,
+ __find_interface);
return argb.interface;
}
-#ifdef CONFIG_HOTPLUG
-
-/*
- * This sends an uevent to userspace, typically helping to load driver
- * or other modules, configure the device, and more. Drivers can provide
- * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+/**
+ * usb_release_dev - free a usb device structure when all users of it are finished.
+ * @dev: device that's been disconnected
*
- * We're called either from khubd (the typical case) or from root hub
- * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
- * delays in event delivery. Use sysfs (and DEVPATH) to make sure the
- * device (and this configuration!) are still present.
+ * Will be called only by the device core when all users of this usb device are
+ * done.
*/
-static int usb_uevent(struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+static void usb_release_dev(struct device *dev)
{
- struct usb_interface *intf;
- struct usb_device *usb_dev;
- struct usb_host_interface *alt;
- int i = 0;
- int length = 0;
-
- if (!dev)
- return -ENODEV;
-
- /* driver is often null here; dev_dbg() would oops */
- pr_debug ("usb %s: uevent\n", dev->bus_id);
-
- /* Must check driver_data here, as on remove driver is always NULL */
- if ((dev->driver == &usb_generic_driver) ||
- (dev->driver_data == &usb_generic_driver_data))
- return 0;
-
- intf = to_usb_interface(dev);
- usb_dev = interface_to_usbdev (intf);
- alt = intf->cur_altsetting;
+ struct usb_device *udev;
- if (usb_dev->devnum < 0) {
- pr_debug ("usb %s: already deleted?\n", dev->bus_id);
- return -ENODEV;
- }
- if (!usb_dev->bus) {
- pr_debug ("usb %s: bus removed?\n", dev->bus_id);
- return -ENODEV;
- }
+ udev = to_usb_device(dev);
-#ifdef CONFIG_USB_DEVICEFS
- /* If this is available, userspace programs can directly read
- * all the device descriptors we don't tell them about. Or
- * even act as usermode drivers.
- *
- * FIXME reduce hardwired intelligence here
- */
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "DEVICE=/proc/bus/usb/%03d/%03d",
- usb_dev->bus->busnum, usb_dev->devnum))
- return -ENOMEM;
+#ifdef CONFIG_USB_SUSPEND
+ cancel_delayed_work(&udev->autosuspend);
+ flush_workqueue(ksuspend_usb_wq);
#endif
+ usb_destroy_configuration(udev);
+ usb_put_hcd(bus_to_hcd(udev->bus));
+ kfree(udev->product);
+ kfree(udev->manufacturer);
+ kfree(udev->serial);
+ kfree(udev);
+}
- /* per-device configurations are common */
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "PRODUCT=%x/%x/%x",
- le16_to_cpu(usb_dev->descriptor.idVendor),
- le16_to_cpu(usb_dev->descriptor.idProduct),
- le16_to_cpu(usb_dev->descriptor.bcdDevice)))
- return -ENOMEM;
+#ifdef CONFIG_PM
- /* class-based driver binding models */
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "TYPE=%d/%d/%d",
- usb_dev->descriptor.bDeviceClass,
- usb_dev->descriptor.bDeviceSubClass,
- usb_dev->descriptor.bDeviceProtocol))
+static int ksuspend_usb_init(void)
+{
+ ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd");
+ if (!ksuspend_usb_wq)
return -ENOMEM;
+ return 0;
+}
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "INTERFACE=%d/%d/%d",
- alt->desc.bInterfaceClass,
- alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
- return -ENOMEM;
+static void ksuspend_usb_cleanup(void)
+{
+ destroy_workqueue(ksuspend_usb_wq);
+}
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
- le16_to_cpu(usb_dev->descriptor.idVendor),
- le16_to_cpu(usb_dev->descriptor.idProduct),
- le16_to_cpu(usb_dev->descriptor.bcdDevice),
- usb_dev->descriptor.bDeviceClass,
- usb_dev->descriptor.bDeviceSubClass,
- usb_dev->descriptor.bDeviceProtocol,
- alt->desc.bInterfaceClass,
- alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
- return -ENOMEM;
+#else
- envp[i] = NULL;
+#define ksuspend_usb_init() 0
+#define ksuspend_usb_cleanup() do {} while (0)
- return 0;
-}
+#endif
-#else
+#ifdef CONFIG_USB_SUSPEND
-static int usb_uevent(struct device *dev, char **envp,
- int num_envp, char *buffer, int buffer_size)
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+static void usb_autosuspend_work(void *_udev)
{
- return -ENODEV;
-}
+ struct usb_device *udev = _udev;
-#endif /* CONFIG_HOTPLUG */
+ usb_pm_lock(udev);
+ udev->auto_pm = 1;
+ usb_suspend_both(udev, PMSG_SUSPEND);
+ usb_pm_unlock(udev);
+}
-/**
- * usb_release_dev - free a usb device structure when all users of it are finished.
- * @dev: device that's been disconnected
- *
- * Will be called only by the device core when all users of this usb device are
- * done.
- */
-static void usb_release_dev(struct device *dev)
-{
- struct usb_device *udev;
+#else
- udev = to_usb_device(dev);
+static void usb_autosuspend_work(void *_udev)
+{}
- usb_destroy_configuration(udev);
- usb_bus_put(udev->bus);
- kfree(udev->product);
- kfree(udev->manufacturer);
- kfree(udev->serial);
- kfree(udev);
-}
+#endif
/**
* usb_alloc_dev - usb device constructor (usbcore-internal)
@@ -390,8 +248,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
if (!dev)
return NULL;
- bus = usb_bus_get(bus);
- if (!bus) {
+ if (!usb_get_hcd(bus_to_hcd(bus))) {
kfree(dev);
return NULL;
}
@@ -399,11 +256,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.dma_mask = bus->controller->dma_mask;
- dev->dev.driver_data = &usb_generic_driver_data;
- dev->dev.driver = &usb_generic_driver;
dev->dev.release = usb_release_dev;
dev->state = USB_STATE_ATTACHED;
+ /* This magic assignment distinguishes devices from interfaces */
+ dev->dev.platform_data = &usb_generic_driver;
+
INIT_LIST_HEAD(&dev->ep0.urb_list);
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
@@ -444,6 +302,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
+#ifdef CONFIG_PM
+ mutex_init(&dev->pm_mutex);
+ INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev);
+#endif
return dev;
}
@@ -549,7 +411,7 @@ void usb_put_intf(struct usb_interface *intf)
* case the driver already owns the device lock.)
*/
int usb_lock_device_for_reset(struct usb_device *udev,
- struct usb_interface *iface)
+ const struct usb_interface *iface)
{
unsigned long jiffies_expire = jiffies + HZ;
@@ -672,7 +534,139 @@ exit:
*/
int usb_get_current_frame_number(struct usb_device *dev)
{
- return dev->bus->op->get_frame_number (dev);
+ return usb_hcd_get_frame_number (dev);
+}
+
+/**
+ * usb_endpoint_dir_in - check if the endpoint has IN direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type IN, otherwise it returns false.
+ */
+int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
+}
+
+/**
+ * usb_endpoint_dir_out - check if the endpoint has OUT direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type OUT, otherwise it returns false.
+ */
+int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+}
+
+/**
+ * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type bulk, otherwise it returns false.
+ */
+int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK);
+}
+
+/**
+ * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type interrupt, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT);
+}
+
+/**
+ * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type isochronous, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_ISOC);
+}
+
+/**
+ * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_int_in - check if the endpoint is interrupt IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));
}
/*-------------------------------------------------------------------*/
@@ -737,9 +731,9 @@ void *usb_buffer_alloc (
dma_addr_t *dma
)
{
- if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+ if (!dev || !dev->bus)
return NULL;
- return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
+ return hcd_buffer_alloc (dev->bus, size, mem_flags, dma);
}
/**
@@ -760,9 +754,11 @@ void usb_buffer_free (
dma_addr_t dma
)
{
- if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
- return;
- dev->bus->op->buffer_free (dev->bus, size, addr, dma);
+ if (!dev || !dev->bus)
+ return;
+ if (!addr)
+ return;
+ hcd_buffer_free (dev->bus, size, addr, dma);
}
/**
@@ -911,8 +907,8 @@ void usb_buffer_unmap (struct urb *urb)
*
* Reverse the effect of this call with usb_buffer_unmap_sg().
*/
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
- struct scatterlist *sg, int nents)
+int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int nents)
{
struct usb_bus *bus;
struct device *controller;
@@ -946,8 +942,8 @@ int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
* Use this when you are re-using a scatterlist's data buffers for
* another USB request.
*/
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
- struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
@@ -972,8 +968,8 @@ void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
*
* Reverses the effect of usb_buffer_map_sg().
*/
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
- struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
@@ -988,116 +984,6 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
-static int verify_suspended(struct device *dev, void *unused)
-{
- if (dev->driver == NULL)
- return 0;
- return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
-}
-
-static int usb_generic_suspend(struct device *dev, pm_message_t message)
-{
- struct usb_interface *intf;
- struct usb_driver *driver;
- int status;
-
- /* USB devices enter SUSPEND state through their hubs, but can be
- * marked for FREEZE as soon as their children are already idled.
- * But those semantics are useless, so we equate the two (sigh).
- */
- if (dev->driver == &usb_generic_driver) {
- if (dev->power.power_state.event == message.event)
- return 0;
- /* we need to rule out bogus requests through sysfs */
- status = device_for_each_child(dev, NULL, verify_suspended);
- if (status)
- return status;
- return usb_suspend_device (to_usb_device(dev));
- }
-
- if ((dev->driver == NULL) ||
- (dev->driver_data == &usb_generic_driver_data))
- return 0;
-
- intf = to_usb_interface(dev);
- driver = to_usb_driver(dev->driver);
-
- /* with no hardware, USB interfaces only use FREEZE and ON states */
- if (!is_active(intf))
- return 0;
-
- if (driver->suspend && driver->resume) {
- status = driver->suspend(intf, message);
- if (status)
- dev_err(dev, "%s error %d\n", "suspend", status);
- else
- mark_quiesced(intf);
- } else {
- // FIXME else if there's no suspend method, disconnect...
- dev_warn(dev, "no suspend for driver %s?\n", driver->name);
- mark_quiesced(intf);
- status = 0;
- }
- return status;
-}
-
-static int usb_generic_resume(struct device *dev)
-{
- struct usb_interface *intf;
- struct usb_driver *driver;
- struct usb_device *udev;
- int status;
-
- if (dev->power.power_state.event == PM_EVENT_ON)
- return 0;
-
- /* mark things as "on" immediately, no matter what errors crop up */
- dev->power.power_state.event = PM_EVENT_ON;
-
- /* devices resume through their hubs */
- if (dev->driver == &usb_generic_driver) {
- udev = to_usb_device(dev);
- if (udev->state == USB_STATE_NOTATTACHED)
- return 0;
- return usb_resume_device (to_usb_device(dev));
- }
-
- if ((dev->driver == NULL) ||
- (dev->driver_data == &usb_generic_driver_data)) {
- dev->power.power_state.event = PM_EVENT_FREEZE;
- return 0;
- }
-
- intf = to_usb_interface(dev);
- driver = to_usb_driver(dev->driver);
-
- udev = interface_to_usbdev(intf);
- if (udev->state == USB_STATE_NOTATTACHED)
- return 0;
-
- /* if driver was suspended, it has a resume method;
- * however, sysfs can wrongly mark things as suspended
- * (on the "no suspend method" FIXME path above)
- */
- if (driver->resume) {
- status = driver->resume(intf);
- if (status) {
- dev_err(dev, "%s error %d\n", "resume", status);
- mark_quiesced(intf);
- }
- } else
- dev_warn(dev, "no resume for driver %s?\n", driver->name);
- return 0;
-}
-
-struct bus_type usb_bus_type = {
- .name = "usb",
- .match = usb_device_match,
- .uevent = usb_uevent,
- .suspend = usb_generic_suspend,
- .resume = usb_generic_resume,
-};
-
/* format to disable USB on kernel command line is: nousb */
__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
@@ -1120,9 +1006,12 @@ static int __init usb_init(void)
return 0;
}
+ retval = ksuspend_usb_init();
+ if (retval)
+ goto out;
retval = bus_register(&usb_bus_type);
if (retval)
- goto out;
+ goto bus_register_failed;
retval = usb_host_init();
if (retval)
goto host_init_failed;
@@ -1141,7 +1030,7 @@ static int __init usb_init(void)
retval = usb_hub_init();
if (retval)
goto hub_init_failed;
- retval = driver_register(&usb_generic_driver);
+ retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
goto out;
@@ -1158,6 +1047,8 @@ major_init_failed:
usb_host_cleanup();
host_init_failed:
bus_unregister(&usb_bus_type);
+bus_register_failed:
+ ksuspend_usb_cleanup();
out:
return retval;
}
@@ -1171,7 +1062,7 @@ static void __exit usb_exit(void)
if (nousb)
return;
- driver_unregister(&usb_generic_driver);
+ usb_deregister_device_driver(&usb_generic_driver);
usb_major_cleanup();
usbfs_cleanup();
usb_deregister(&usbfs_driver);
@@ -1179,6 +1070,7 @@ static void __exit usb_exit(void)
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister(&usb_bus_type);
+ ksuspend_usb_cleanup();
}
subsys_initcall(usb_init);
@@ -1201,20 +1093,27 @@ EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
EXPORT_SYMBOL(usb_lock_device_for_reset);
-EXPORT_SYMBOL(usb_driver_claim_interface);
-EXPORT_SYMBOL(usb_driver_release_interface);
EXPORT_SYMBOL(usb_find_interface);
EXPORT_SYMBOL(usb_ifnum_to_if);
EXPORT_SYMBOL(usb_altnum_to_altsetting);
-EXPORT_SYMBOL(usb_reset_device);
-EXPORT_SYMBOL(usb_reset_composite_device);
-
EXPORT_SYMBOL(__usb_get_extra_descriptor);
EXPORT_SYMBOL(usb_find_device);
EXPORT_SYMBOL(usb_get_current_frame_number);
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_bulk);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_int);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_isoc);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_out);
+
EXPORT_SYMBOL (usb_buffer_alloc);
EXPORT_SYMBOL (usb_buffer_free);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 49f69236b42..f69df137ec0 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,10 +1,10 @@
/* Functions local to drivers/usb/core/ */
-extern void usb_create_sysfs_dev_files (struct usb_device *dev);
+extern int usb_create_sysfs_dev_files (struct usb_device *dev);
extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
-extern void usb_create_sysfs_intf_files (struct usb_interface *intf);
+extern int usb_create_sysfs_intf_files (struct usb_interface *intf);
extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
-extern void usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
+extern int usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
struct usb_device *udev);
extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
@@ -20,7 +20,6 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern void usb_kick_khubd(struct usb_device *dev);
-extern void usb_suspend_root_hub(struct usb_device *hdev);
extern void usb_resume_root_hub(struct usb_device *dev);
extern int usb_hub_init(void);
@@ -30,28 +29,91 @@ extern void usb_major_cleanup(void);
extern int usb_host_init(void);
extern void usb_host_cleanup(void);
-extern int usb_suspend_device(struct usb_device *dev);
-extern int usb_resume_device(struct usb_device *dev);
+#ifdef CONFIG_PM
-extern struct device_driver usb_generic_driver;
-extern int usb_generic_driver_data;
-extern int usb_device_match(struct device *dev, struct device_driver *drv);
+extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
+extern int usb_resume_both(struct usb_device *udev);
+extern int usb_port_suspend(struct usb_device *dev);
+extern int usb_port_resume(struct usb_device *dev);
+
+static inline void usb_pm_lock(struct usb_device *udev)
+{
+ mutex_lock_nested(&udev->pm_mutex, udev->level);
+}
+
+static inline void usb_pm_unlock(struct usb_device *udev)
+{
+ mutex_unlock(&udev->pm_mutex);
+}
+
+#else
+
+#define usb_suspend_both(udev, msg) 0
+static inline int usb_resume_both(struct usb_device *udev)
+{
+ return 0;
+}
+#define usb_port_suspend(dev) 0
+#define usb_port_resume(dev) 0
+static inline void usb_pm_lock(struct usb_device *udev) {}
+static inline void usb_pm_unlock(struct usb_device *udev) {}
+
+#endif
+
+#ifdef CONFIG_USB_SUSPEND
+
+#define USB_AUTOSUSPEND_DELAY (HZ*2)
+
+extern void usb_autosuspend_device(struct usb_device *udev, int dec_busy_cnt);
+extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt);
+
+#else
+
+#define usb_autosuspend_device(udev, dec_busy_cnt) do {} while (0)
+static inline int usb_autoresume_device(struct usb_device *udev,
+ int inc_busy_cnt)
+{
+ return 0;
+}
+
+#endif
+
+extern struct workqueue_struct *ksuspend_usb_wq;
+extern struct bus_type usb_bus_type;
+extern struct usb_device_driver usb_generic_driver;
+
+/* Here's how we tell apart devices and interfaces. Luckily there's
+ * no such thing as a platform USB device, so we can steal the use
+ * of the platform_data field. */
+
+static inline int is_usb_device(const struct device *dev)
+{
+ return dev->platform_data == &usb_generic_driver;
+}
+
+/* Do the same for device drivers and interface drivers. */
+
+static inline int is_usb_device_driver(struct device_driver *drv)
+{
+ return container_of(drv, struct usbdrv_wrap, driver)->
+ for_devices;
+}
/* Interfaces and their "power state" are owned by usbcore */
static inline void mark_active(struct usb_interface *f)
{
- f->dev.power.power_state.event = PM_EVENT_ON;
+ f->is_active = 1;
}
static inline void mark_quiesced(struct usb_interface *f)
{
- f->dev.power.power_state.event = PM_EVENT_FREEZE;
+ f->is_active = 0;
}
-static inline int is_active(struct usb_interface *f)
+static inline int is_active(const struct usb_interface *f)
{
- return f->dev.power.power_state.event == PM_EVENT_ON;
+ return f->is_active;
}
@@ -59,9 +121,10 @@ static inline int is_active(struct usb_interface *f)
extern const char *usbcore_name;
/* usbfs stuff */
+extern struct mutex usbfs_mutex;
extern struct usb_driver usbfs_driver;
-extern struct file_operations usbfs_devices_fops;
-extern struct file_operations usbfs_device_file_operations;
+extern const struct file_operations usbfs_devices_fops;
+extern const struct file_operations usbfs_device_file_operations;
extern void usbfs_conn_disc_event(void);
extern int usbdev_init(void);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 1a32d96774b..8e5dd6f29d0 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -26,7 +26,7 @@ config USB_GADGET
you need a low level bus controller driver, and some software
talking to it. Peripheral controllers are often discrete silicon,
or are integrated with the CPU in a microcontroller. The more
- familiar host side controllers have names like like "EHCI", "OHCI",
+ familiar host side controllers have names like "EHCI", "OHCI",
or "UHCI", and are usually integrated into southbridges on PC
motherboards.
@@ -404,6 +404,20 @@ config USB_G_SERIAL
which includes instructions and a "driver info file" needed to
make MS-Windows work with this driver.
+config USB_MIDI_GADGET
+ tristate "MIDI Gadget (EXPERIMENTAL)"
+ depends on SND && EXPERIMENTAL
+ select SND_RAWMIDI
+ help
+ The MIDI Gadget acts as a USB Audio device, with one MIDI
+ input and one MIDI output. These MIDI jacks appear as
+ a sound "card" in the ALSA sound system. Other MIDI
+ connections can then be made on the gadget system, using
+ ALSA's aconnect utility etc.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_midi".
+
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5a28e61392e..e71e086a1cf 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_USB_AT91) += at91_udc.o
g_zero-objs := zero.o usbstring.o config.o epautoconf.o
g_ether-objs := ether.o usbstring.o config.o epautoconf.o
g_serial-objs := serial.o usbstring.o config.o epautoconf.o
+g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o
gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o usbstring.o config.o \
epautoconf.o
@@ -28,4 +29,5 @@ obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index cfebca05ead..77beba485a8 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -247,7 +247,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
return single_open(file, proc_udc_show, PDE(inode)->data);
}
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
.open = proc_udc_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -1658,7 +1658,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (!request_mem_region(AT91_BASE_UDP, SZ_16K, driver_name)) {
+ if (!request_mem_region(AT91RM9200_BASE_UDP, SZ_16K, driver_name)) {
DBG("someone's using UDC memory\n");
return -EBUSY;
}
@@ -1720,7 +1720,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev)
fail1:
device_unregister(&udc->gadget.dev);
fail0:
- release_mem_region(AT91_BASE_UDP, SZ_16K);
+ release_mem_region(AT91RM9200_BASE_UDP, SZ_16K);
DBG("%s probe failed, %d\n", driver_name, retval);
return retval;
}
@@ -1742,7 +1742,7 @@ static int __devexit at91udc_remove(struct platform_device *pdev)
free_irq(udc->board.vbus_pin, udc);
free_irq(udc->udp_irq, udc);
device_unregister(&udc->gadget.dev);
- release_mem_region(AT91_BASE_UDP, SZ_16K);
+ release_mem_region(AT91RM9200_BASE_UDP, SZ_16K);
clk_put(udc->iclk);
clk_put(udc->fclk);
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 7d1c22c3495..4d2946e540c 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -816,15 +816,14 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
dum->gadget.dev.driver = &driver->driver;
dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n",
driver->driver.name);
- if ((retval = driver->bind (&dum->gadget)) != 0) {
- dum->driver = NULL;
- dum->gadget.dev.driver = NULL;
- return retval;
- }
+ if ((retval = driver->bind (&dum->gadget)) != 0)
+ goto err_bind_gadget;
driver->driver.bus = dum->gadget.dev.parent->bus;
- driver_register (&driver->driver);
- device_bind_driver (&dum->gadget.dev);
+ if ((retval = driver_register (&driver->driver)) != 0)
+ goto err_register;
+ if ((retval = device_bind_driver (&dum->gadget.dev)) != 0)
+ goto err_bind_driver;
/* khubd will enumerate this in a while */
spin_lock_irq (&dum->lock);
@@ -834,6 +833,19 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
+
+err_bind_driver:
+ driver_unregister (&driver->driver);
+err_register:
+ driver->unbind (&dum->gadget);
+ spin_lock_irq (&dum->lock);
+ dum->pullup = 0;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+err_bind_gadget:
+ dum->driver = NULL;
+ dum->gadget.dev.driver = NULL;
+ return retval;
}
EXPORT_SYMBOL (usb_gadget_register_driver);
@@ -889,11 +901,9 @@ EXPORT_SYMBOL (net2280_set_fifo_mode);
static void
dummy_gadget_release (struct device *dev)
{
-#if 0 /* usb_bus_put isn't EXPORTed! */
struct dummy *dum = gadget_dev_to_dummy (dev);
- usb_bus_put (&dummy_to_hcd (dum)->self);
-#endif
+ usb_put_hcd (dummy_to_hcd (dum));
}
static int dummy_udc_probe (struct platform_device *pdev)
@@ -915,12 +925,12 @@ static int dummy_udc_probe (struct platform_device *pdev)
if (rc < 0)
return rc;
-#if 0 /* usb_bus_get isn't EXPORTed! */
- usb_bus_get (&dummy_to_hcd (dum)->self);
-#endif
+ usb_get_hcd (dummy_to_hcd (dum));
platform_set_drvdata (pdev, dum);
- device_create_file (&dum->gadget.dev, &dev_attr_function);
+ rc = device_create_file (&dum->gadget.dev, &dev_attr_function);
+ if (rc < 0)
+ device_unregister (&dum->gadget.dev);
return rc;
}
@@ -1868,8 +1878,7 @@ static int dummy_start (struct usb_hcd *hcd)
#endif
/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
- device_create_file (dummy_dev(dum), &dev_attr_urbs);
- return 0;
+ return device_create_file (dummy_dev(dum), &dev_attr_urbs);
}
static void dummy_stop (struct usb_hcd *hcd)
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 30299c620d9..366dc0a9e52 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -262,7 +262,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
-#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
#define DEV_CONFIG_CDC
#endif
@@ -2014,7 +2014,7 @@ rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
static int rndis_control_ack (struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
- u32 length;
+ int length;
struct usb_request *resp = dev->stat_req;
/* in case RNDIS calls this after disconnect */
@@ -2230,6 +2230,9 @@ eth_bind (struct usb_gadget *gadget)
if (gadget_is_pxa (gadget)) {
/* pxa doesn't support altsettings */
cdc = 0;
+ } else if (gadget_is_musbhdrc(gadget)) {
+ /* reduce tx dma overhead by avoiding special cases */
+ zlp = 0;
} else if (gadget_is_sh(gadget)) {
/* sh doesn't support multiple interfaces or configs */
cdc = 0;
@@ -2564,7 +2567,7 @@ static struct usb_gadget_driver eth_driver = {
.function = (char *) driver_desc,
.bind = eth_bind,
- .unbind = __exit_p(eth_unbind),
+ .unbind = eth_unbind,
.setup = eth_setup,
.disconnect = eth_disconnect,
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 8d7f1e84cd7..c83d3b6c68f 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -567,6 +567,7 @@ struct lun {
unsigned int ro : 1;
unsigned int prevent_medium_removal : 1;
unsigned int registered : 1;
+ unsigned int info_valid : 1;
u32 sense_data;
u32 sense_data_info;
@@ -1656,6 +1657,7 @@ static int do_read(struct fsg_dev *fsg)
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
bh->inreq->length = 0;
bh->state = BUF_STATE_FULL;
break;
@@ -1691,6 +1693,7 @@ static int do_read(struct fsg_dev *fsg)
if (nread < amount) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -1785,6 +1788,7 @@ static int do_write(struct fsg_dev *fsg)
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = usb_offset >> 9;
+ curlun->info_valid = 1;
continue;
}
amount -= (amount & 511);
@@ -1827,6 +1831,7 @@ static int do_write(struct fsg_dev *fsg)
if (bh->outreq->status != 0) {
curlun->sense_data = SS_COMMUNICATION_FAILURE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -1868,6 +1873,7 @@ static int do_write(struct fsg_dev *fsg)
if (nwritten < amount) {
curlun->sense_data = SS_WRITE_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -2010,6 +2016,7 @@ static int do_verify(struct fsg_dev *fsg)
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -2036,6 +2043,7 @@ static int do_verify(struct fsg_dev *fsg)
if (nread == 0) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
file_offset += nread;
@@ -2079,6 +2087,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
struct lun *curlun = fsg->curlun;
u8 *buf = (u8 *) bh->buf;
u32 sd, sdinfo;
+ int valid;
/*
* From the SCSI-2 spec., section 7.9 (Unit attention condition):
@@ -2106,15 +2115,18 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
fsg->bad_lun_okay = 1;
sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
sdinfo = 0;
+ valid = 0;
} else {
sd = curlun->sense_data;
sdinfo = curlun->sense_data_info;
+ valid = curlun->info_valid << 7;
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
memset(buf, 0, 18);
- buf[0] = 0x80 | 0x70; // Valid, current error
+ buf[0] = valid | 0x70; // Valid, current error
buf[2] = SK(sd);
put_be32(&buf[3], sdinfo); // Sense information
buf[7] = 18 - 8; // Additional sense length
@@ -2703,6 +2715,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
if (fsg->cmnd[0] != SC_REQUEST_SENSE) {
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
} else {
fsg->curlun = curlun = NULL;
@@ -3332,6 +3345,7 @@ static void handle_exception(struct fsg_dev *fsg)
curlun->sense_data = curlun->unit_attention_data =
SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
fsg->state = FSG_STATE_IDLE;
}
@@ -3873,21 +3887,26 @@ static int __init fsg_bind(struct usb_gadget *gadget)
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
curlun->ro = mod_data.ro[i];
+ curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
dev_set_drvdata(&curlun->dev, fsg);
snprintf(curlun->dev.bus_id, BUS_ID_SIZE,
"%s-lun%d", gadget->dev.bus_id, i);
- if ((rc = device_register(&curlun->dev)) != 0)
+ if ((rc = device_register(&curlun->dev)) != 0) {
INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
- else {
- curlun->registered = 1;
- curlun->dev.release = lun_release;
- device_create_file(&curlun->dev, &dev_attr_ro);
- device_create_file(&curlun->dev, &dev_attr_file);
- kref_get(&fsg->ref);
+ goto out;
+ }
+ if ((rc = device_create_file(&curlun->dev,
+ &dev_attr_ro)) != 0 ||
+ (rc = device_create_file(&curlun->dev,
+ &dev_attr_file)) != 0) {
+ device_unregister(&curlun->dev);
+ goto out;
}
+ curlun->registered = 1;
+ kref_get(&fsg->ref);
if (mod_data.file[i] && *mod_data.file[i]) {
if ((rc = open_backing_file(curlun,
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
new file mode 100644
index 00000000000..b68cecd5741
--- /dev/null
+++ b/drivers/usb/gadget/gmidi.c
@@ -0,0 +1,1337 @@
+/*
+ * gmidi.c -- USB MIDI Gadget Driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This code is based in part on:
+ *
+ * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell.
+ * USB Audio driver, Copyright (C) 2002 by Takashi Iwai.
+ * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch.
+ *
+ * Refer to the USB Device Class Definition for MIDI Devices:
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ */
+
+#define DEBUG 1
+// #define VERBOSE
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+#include "gadget_chips.h"
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+#define DRIVER_VERSION "25 Jul 2006"
+
+static const char shortname[] = "g_midi";
+static const char longname[] = "MIDI Gadget";
+
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
+
+/* Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both. These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, S_IRUGO);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, S_IRUGO);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, S_IRUGO);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
+
+/*
+ * this version autoconfigures as much as possible,
+ * which is reasonable for most "bulk-only" drivers.
+ */
+static const char *EP_IN_NAME;
+static const char *EP_OUT_NAME;
+
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ 256
+
+
+/* This is a gadget, and the IN/OUT naming is from the host's perspective.
+ USB -> OUT endpoint -> rawmidi
+ USB <- IN endpoint <- rawmidi */
+struct gmidi_in_port {
+ struct gmidi_device* dev;
+ int active;
+ uint8_t cable; /* cable number << 4 */
+ uint8_t state;
+#define STATE_UNKNOWN 0
+#define STATE_1PARAM 1
+#define STATE_2PARAM_1 2
+#define STATE_2PARAM_2 3
+#define STATE_SYSEX_0 4
+#define STATE_SYSEX_1 5
+#define STATE_SYSEX_2 6
+ uint8_t data[2];
+};
+
+struct gmidi_device {
+ spinlock_t lock;
+ struct usb_gadget *gadget;
+ struct usb_request *req; /* for control responses */
+ u8 config;
+ struct usb_ep *in_ep, *out_ep;
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *in_substream;
+ struct snd_rawmidi_substream *out_substream;
+
+ /* For the moment we only support one port in
+ each direction, but in_port is kept as a
+ separate struct so we can have more later. */
+ struct gmidi_in_port in_port;
+ unsigned long out_triggered;
+ struct tasklet_struct tasklet;
+};
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
+
+
+#define xprintk(d,level,fmt,args...) \
+ dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+ xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(dev,fmt,args...) \
+ do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+ xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+ xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+ xprintk(dev , KERN_INFO , fmt , ## args)
+
+
+static unsigned buflen = 256;
+static unsigned qlen = 32;
+
+module_param(buflen, uint, S_IRUGO);
+module_param(qlen, uint, S_IRUGO);
+
+
+/* Thanks to Grey Innovation for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */
+#define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */
+
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full)
+ * configuration descriptors are built on demand.
+ */
+
+#define STRING_MANUFACTURER 25
+#define STRING_PRODUCT 42
+#define STRING_SERIAL 101
+#define STRING_MIDI_GADGET 250
+
+/* We only have the one configuration, it's number 1. */
+#define GMIDI_CONFIG 1
+
+/* We have two interfaces- AudioControl and MIDIStreaming */
+#define GMIDI_AC_INTERFACE 0
+#define GMIDI_MS_INTERFACE 1
+#define GMIDI_NUM_INTERFACES 2
+
+DECLARE_USB_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
+
+/* B.1 Device Descriptor */
+static struct usb_device_descriptor device_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .bNumConfigurations = 1,
+};
+
+/* B.2 Configuration Descriptor */
+static struct usb_config_descriptor config_desc = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+ /* compute wTotalLength on the fly */
+ .bNumInterfaces = GMIDI_NUM_INTERFACES,
+ .bConfigurationValue = GMIDI_CONFIG,
+ .iConfiguration = STRING_MIDI_GADGET,
+ /*
+ * FIXME: When embedding this driver in a device,
+ * these need to be set to reflect the actual
+ * power properties of the device. Is it selfpowered?
+ */
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .bMaxPower = 1,
+};
+
+/* B.3.1 Standard AC Interface Descriptor */
+static const struct usb_interface_descriptor ac_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = GMIDI_AC_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .iInterface = STRING_MIDI_GADGET,
+};
+
+/* B.3.2 Class-Specific AC Interface Descriptor */
+static const struct usb_ac_header_descriptor_1 ac_header_desc = {
+ .bLength = USB_DT_AC_HEADER_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdADC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = USB_DT_AC_HEADER_SIZE(1),
+ .bInCollection = 1,
+ .baInterfaceNr = {
+ [0] = GMIDI_MS_INTERFACE,
+ }
+};
+
+/* B.4.1 Standard MS Interface Descriptor */
+static const struct usb_interface_descriptor ms_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = GMIDI_MS_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .iInterface = STRING_MIDI_GADGET,
+};
+
+/* B.4.2 Class-Specific MS Interface Descriptor */
+static const struct usb_ms_header_descriptor ms_header_desc = {
+ .bLength = USB_DT_MS_HEADER_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = USB_DT_MS_HEADER_SIZE
+ + 2*USB_DT_MIDI_IN_SIZE
+ + 2*USB_DT_MIDI_OUT_SIZE(1),
+};
+
+#define JACK_IN_EMB 1
+#define JACK_IN_EXT 2
+#define JACK_OUT_EMB 3
+#define JACK_OUT_EXT 4
+
+/* B.4.3 MIDI IN Jack Descriptors */
+static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = {
+ .bLength = USB_DT_MIDI_IN_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = JACK_IN_EMB,
+};
+
+static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = {
+ .bLength = USB_DT_MIDI_IN_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = JACK_IN_EXT,
+};
+
+/* B.4.4 MIDI OUT Jack Descriptors */
+static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = {
+ .bLength = USB_DT_MIDI_OUT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = JACK_OUT_EMB,
+ .bNrInputPins = 1,
+ .pins = {
+ [0] = {
+ .baSourceID = JACK_IN_EXT,
+ .baSourcePin = 1,
+ }
+ }
+};
+
+static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = {
+ .bLength = USB_DT_MIDI_OUT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = JACK_OUT_EXT,
+ .bNrInputPins = 1,
+ .pins = {
+ [0] = {
+ .baSourceID = JACK_IN_EMB,
+ .baSourcePin = 1,
+ }
+ }
+};
+
+/* B.5.1 Standard Bulk OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = {
+ .bLength = USB_DT_MS_ENDPOINT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = {
+ [0] = JACK_IN_EMB,
+ }
+};
+
+/* B.6.1 Standard Bulk IN Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = {
+ .bLength = USB_DT_MS_ENDPOINT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = {
+ [0] = JACK_OUT_EMB,
+ }
+};
+
+static const struct usb_descriptor_header *gmidi_function [] = {
+ (struct usb_descriptor_header *)&ac_interface_desc,
+ (struct usb_descriptor_header *)&ac_header_desc,
+ (struct usb_descriptor_header *)&ms_interface_desc,
+
+ (struct usb_descriptor_header *)&ms_header_desc,
+ (struct usb_descriptor_header *)&jack_in_emb_desc,
+ (struct usb_descriptor_header *)&jack_in_ext_desc,
+ (struct usb_descriptor_header *)&jack_out_emb_desc,
+ (struct usb_descriptor_header *)&jack_out_ext_desc,
+ /* If you add more jacks, update ms_header_desc.wTotalLength */
+
+ (struct usb_descriptor_header *)&bulk_out_desc,
+ (struct usb_descriptor_header *)&ms_out_desc,
+ (struct usb_descriptor_header *)&bulk_in_desc,
+ (struct usb_descriptor_header *)&ms_in_desc,
+ NULL,
+};
+
+static char manufacturer[50];
+static char product_desc[40] = "MIDI Gadget";
+static char serial_number[20];
+
+/* static strings, in UTF-8 */
+static struct usb_string strings [] = {
+ { STRING_MANUFACTURER, manufacturer, },
+ { STRING_PRODUCT, product_desc, },
+ { STRING_SERIAL, serial_number, },
+ { STRING_MIDI_GADGET, longname, },
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = strings,
+};
+
+static int config_buf(struct usb_gadget *gadget,
+ u8 *buf, u8 type, unsigned index)
+{
+ int len;
+
+ /* only one configuration */
+ if (index != 0) {
+ return -EINVAL;
+ }
+ len = usb_gadget_config_buf(&config_desc,
+ buf, USB_BUFSIZ, gmidi_function);
+ if (len < 0) {
+ return len;
+ }
+ ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
+ return len;
+}
+
+static struct usb_request* alloc_ep_req(struct usb_ep *ep, unsigned length)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (req) {
+ req->length = length;
+ req->buf = kmalloc(length, GFP_ATOMIC);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+static const uint8_t gmidi_cin_length[] = {
+ 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Receives a chunk of MIDI data.
+ */
+static void gmidi_read_data(struct usb_ep *ep, int cable,
+ uint8_t* data, int length)
+{
+ struct gmidi_device *dev = ep->driver_data;
+ /* cable is ignored, because for now we only have one. */
+
+ if (!dev->out_substream) {
+ /* Nobody is listening - throw it on the floor. */
+ return;
+ }
+ if (!test_bit(dev->out_substream->number, &dev->out_triggered)) {
+ return;
+ }
+ snd_rawmidi_receive(dev->out_substream, data, length);
+}
+
+static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
+{
+ unsigned i;
+ u8 *buf = req->buf;
+
+ for (i = 0; i + 3 < req->actual; i += 4) {
+ if (buf[i] != 0) {
+ int cable = buf[i] >> 4;
+ int length = gmidi_cin_length[buf[i] & 0x0f];
+ gmidi_read_data(ep, cable, &buf[i + 1], length);
+ }
+ }
+}
+
+static void gmidi_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gmidi_device *dev = ep->driver_data;
+ int status = req->status;
+
+ switch (status) {
+ case 0: /* normal completion */
+ if (ep == dev->out_ep) {
+ /* we received stuff.
+ req is queued again, below */
+ gmidi_handle_out_data(ep, req);
+ } else if (ep == dev->in_ep) {
+ /* our transmit completed.
+ see if there's more to go.
+ gmidi_transmit eats req, don't queue it again. */
+ gmidi_transmit(dev, req);
+ return;
+ }
+ break;
+
+ /* this endpoint is normally active while we're configured */
+ case -ECONNABORTED: /* hardware forced ep reset */
+ case -ECONNRESET: /* request dequeued */
+ case -ESHUTDOWN: /* disconnect from host */
+ VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
+ req->actual, req->length);
+ if (ep == dev->out_ep) {
+ gmidi_handle_out_data(ep, req);
+ }
+ free_ep_req(ep, req);
+ return;
+
+ case -EOVERFLOW: /* buffer overrun on read means that
+ * we didn't provide a big enough
+ * buffer.
+ */
+ default:
+ DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+ break;
+ case -EREMOTEIO: /* short read */
+ break;
+ }
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status) {
+ ERROR(dev, "kill %s: resubmit %d bytes --> %d\n",
+ ep->name, req->length, status);
+ usb_ep_set_halt(ep);
+ /* FIXME recover later ... somehow */
+ }
+}
+
+static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags)
+{
+ int err = 0;
+ struct usb_request *req;
+ struct usb_ep* ep;
+ unsigned i;
+
+ err = usb_ep_enable(dev->in_ep, &bulk_in_desc);
+ if (err) {
+ ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err);
+ goto fail;
+ }
+ dev->in_ep->driver_data = dev;
+
+ err = usb_ep_enable(dev->out_ep, &bulk_out_desc);
+ if (err) {
+ ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err);
+ goto fail;
+ }
+ dev->out_ep->driver_data = dev;
+
+ /* allocate a bunch of read buffers and queue them all at once. */
+ ep = dev->out_ep;
+ for (i = 0; i < qlen && err == 0; i++) {
+ req = alloc_ep_req(ep, buflen);
+ if (req) {
+ req->complete = gmidi_complete;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err) {
+ DBG(dev, "%s queue req: %d\n", ep->name, err);
+ }
+ } else {
+ err = -ENOMEM;
+ }
+ }
+fail:
+ /* caller is responsible for cleanup on error */
+ return err;
+}
+
+
+static void gmidi_reset_config(struct gmidi_device *dev)
+{
+ if (dev->config == 0) {
+ return;
+ }
+
+ DBG(dev, "reset config\n");
+
+ /* just disable endpoints, forcing completion of pending i/o.
+ * all our completion handlers free their requests in this case.
+ */
+ usb_ep_disable(dev->in_ep);
+ usb_ep_disable(dev->out_ep);
+ dev->config = 0;
+}
+
+/* change our operational config. this code must agree with the code
+ * that returns config descriptors, and altsetting code.
+ *
+ * it's also responsible for power management interactions. some
+ * configurations might not work with our current power sources.
+ *
+ * note that some device controller hardware will constrain what this
+ * code can do, perhaps by disallowing more than one configuration or
+ * by limiting configuration choices (like the pxa2xx).
+ */
+static int
+gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags)
+{
+ int result = 0;
+ struct usb_gadget *gadget = dev->gadget;
+
+#if 0
+ /* FIXME */
+ /* Hacking this bit out fixes a bug where on receipt of two
+ USB_REQ_SET_CONFIGURATION messages, we end up with no
+ buffered OUT requests waiting for data. This is clearly
+ hiding a bug elsewhere, because if the config didn't
+ change then we really shouldn't do anything. */
+ /* Having said that, when we do "change" from config 1
+ to config 1, we at least gmidi_reset_config() which
+ clears out any requests on endpoints, so it's not like
+ we leak or anything. */
+ if (number == dev->config) {
+ return 0;
+ }
+#endif
+
+ if (gadget_is_sa1100(gadget) && dev->config) {
+ /* tx fifo is full, but we can't clear it...*/
+ INFO(dev, "can't change configurations\n");
+ return -ESPIPE;
+ }
+ gmidi_reset_config(dev);
+
+ switch (number) {
+ case GMIDI_CONFIG:
+ result = set_gmidi_config(dev, gfp_flags);
+ break;
+ default:
+ result = -EINVAL;
+ /* FALL THROUGH */
+ case 0:
+ return result;
+ }
+
+ if (!result && (!dev->in_ep || !dev->out_ep)) {
+ result = -ENODEV;
+ }
+ if (result) {
+ gmidi_reset_config(dev);
+ } else {
+ char *speed;
+
+ switch (gadget->speed) {
+ case USB_SPEED_LOW: speed = "low"; break;
+ case USB_SPEED_FULL: speed = "full"; break;
+ case USB_SPEED_HIGH: speed = "high"; break;
+ default: speed = "?"; break;
+ }
+
+ dev->config = number;
+ INFO(dev, "%s speed\n", speed);
+ }
+ return result;
+}
+
+
+static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length) {
+ DBG((struct gmidi_device *) ep->driver_data,
+ "setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+ }
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver (like
+ * device and endpoint feature flags, and their status). It's all
+ * housekeeping for the gadget function we're implementing. Most of
+ * the work is in config-specific setup.
+ */
+static int gmidi_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+ struct usb_request *req = dev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* usually this stores reply data in the pre-allocated ep0 buffer,
+ * but config change events will reconfigure hardware.
+ */
+ req->zero = 0;
+ switch (ctrl->bRequest) {
+
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN) {
+ goto unknown;
+ }
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ value = min(w_length, (u16) sizeof(device_desc));
+ memcpy(req->buf, &device_desc, value);
+ break;
+ case USB_DT_CONFIG:
+ value = config_buf(gadget, req->buf,
+ w_value >> 8,
+ w_value & 0xff);
+ if (value >= 0) {
+ value = min(w_length, (u16)value);
+ }
+ break;
+
+ case USB_DT_STRING:
+ /* wIndex == language code.
+ * this driver only handles one language, you can
+ * add string tables for other languages, using
+ * any UTF-8 characters
+ */
+ value = usb_gadget_get_string(&stringtab,
+ w_value & 0xff, req->buf);
+ if (value >= 0) {
+ value = min(w_length, (u16)value);
+ }
+ break;
+ }
+ break;
+
+ /* currently two configs, two speeds */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0) {
+ goto unknown;
+ }
+ if (gadget->a_hnp_support) {
+ DBG(dev, "HNP available\n");
+ } else if (gadget->a_alt_hnp_support) {
+ DBG(dev, "HNP needs a different root port\n");
+ } else {
+ VDBG(dev, "HNP inactive\n");
+ }
+ spin_lock(&dev->lock);
+ value = gmidi_set_config(dev, w_value, GFP_ATOMIC);
+ spin_unlock(&dev->lock);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN) {
+ goto unknown;
+ }
+ *(u8 *)req->buf = dev->config;
+ value = min(w_length, (u16)1);
+ break;
+
+ /* until we add altsetting support, or other interfaces,
+ * only 0/0 are possible. pxa2xx only supports 0/0 (poorly)
+ * and already killed pending endpoint I/O.
+ */
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE) {
+ goto unknown;
+ }
+ spin_lock(&dev->lock);
+ if (dev->config && w_index < GMIDI_NUM_INTERFACES
+ && w_value == 0)
+ {
+ u8 config = dev->config;
+
+ /* resets interface configuration, forgets about
+ * previous transaction state (queued bufs, etc)
+ * and re-inits endpoint state (toggle etc)
+ * no response queued, just zero status == success.
+ * if we had more than one interface we couldn't
+ * use this "reset the config" shortcut.
+ */
+ gmidi_reset_config(dev);
+ gmidi_set_config(dev, config, GFP_ATOMIC);
+ value = 0;
+ }
+ spin_unlock(&dev->lock);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) {
+ goto unknown;
+ }
+ if (!dev->config) {
+ break;
+ }
+ if (w_index >= GMIDI_NUM_INTERFACES) {
+ value = -EDOM;
+ break;
+ }
+ *(u8 *)req->buf = 0;
+ value = min(w_length, (u16)1);
+ break;
+
+ default:
+unknown:
+ VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(dev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ gmidi_setup_complete(gadget->ep0, req);
+ }
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static void gmidi_disconnect(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ gmidi_reset_config(dev);
+
+ /* a more significant application might have some non-usb
+ * activities to quiesce here, saving resources like power
+ * or pushing the notification up a network stack.
+ */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* next we may get setup() calls to enumerate new connections;
+ * or an unbind() during shutdown (including removing module).
+ */
+}
+
+static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+ struct snd_card* card;
+
+ DBG(dev, "unbind\n");
+
+ card = dev->card;
+ dev->card = NULL;
+ if (card) {
+ snd_card_free(card);
+ }
+
+ /* we've already been disconnected ... no i/o is active */
+ if (dev->req) {
+ dev->req->length = USB_BUFSIZ;
+ free_ep_req(gadget->ep0, dev->req);
+ }
+ kfree(dev);
+ set_gadget_data(gadget, NULL);
+}
+
+static int gmidi_snd_free(struct snd_device *device)
+{
+ return 0;
+}
+
+static void gmidi_transmit_packet(struct usb_request* req, uint8_t p0,
+ uint8_t p1, uint8_t p2, uint8_t p3)
+{
+ unsigned length = req->length;
+
+ uint8_t* buf = (uint8_t*)req->buf + length;
+ buf[0] = p0;
+ buf[1] = p1;
+ buf[2] = p2;
+ buf[3] = p3;
+ req->length = length + 4;
+}
+
+/*
+ * Converts MIDI commands to USB MIDI packets.
+ */
+static void gmidi_transmit_byte(struct usb_request* req,
+ struct gmidi_in_port* port, uint8_t b)
+{
+ uint8_t p0 = port->cable;
+
+ if (b >= 0xf8) {
+ gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
+ } else if (b >= 0xf0) {
+ switch (b) {
+ case 0xf0:
+ port->data[0] = b;
+ port->state = STATE_SYSEX_1;
+ break;
+ case 0xf1:
+ case 0xf3:
+ port->data[0] = b;
+ port->state = STATE_1PARAM;
+ break;
+ case 0xf2:
+ port->data[0] = b;
+ port->state = STATE_2PARAM_1;
+ break;
+ case 0xf4:
+ case 0xf5:
+ port->state = STATE_UNKNOWN;
+ break;
+ case 0xf6:
+ gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
+ port->state = STATE_UNKNOWN;
+ break;
+ case 0xf7:
+ switch (port->state) {
+ case STATE_SYSEX_0:
+ gmidi_transmit_packet(req,
+ p0 | 0x05, 0xf7, 0, 0);
+ break;
+ case STATE_SYSEX_1:
+ gmidi_transmit_packet(req,
+ p0 | 0x06, port->data[0], 0xf7, 0);
+ break;
+ case STATE_SYSEX_2:
+ gmidi_transmit_packet(req,
+ p0 | 0x07, port->data[0],
+ port->data[1], 0xf7);
+ break;
+ }
+ port->state = STATE_UNKNOWN;
+ break;
+ }
+ } else if (b >= 0x80) {
+ port->data[0] = b;
+ if (b >= 0xc0 && b <= 0xdf)
+ port->state = STATE_1PARAM;
+ else
+ port->state = STATE_2PARAM_1;
+ } else { /* b < 0x80 */
+ switch (port->state) {
+ case STATE_1PARAM:
+ if (port->data[0] < 0xf0) {
+ p0 |= port->data[0] >> 4;
+ } else {
+ p0 |= 0x02;
+ port->state = STATE_UNKNOWN;
+ }
+ gmidi_transmit_packet(req, p0, port->data[0], b, 0);
+ break;
+ case STATE_2PARAM_1:
+ port->data[1] = b;
+ port->state = STATE_2PARAM_2;
+ break;
+ case STATE_2PARAM_2:
+ if (port->data[0] < 0xf0) {
+ p0 |= port->data[0] >> 4;
+ port->state = STATE_2PARAM_1;
+ } else {
+ p0 |= 0x03;
+ port->state = STATE_UNKNOWN;
+ }
+ gmidi_transmit_packet(req,
+ p0, port->data[0], port->data[1], b);
+ break;
+ case STATE_SYSEX_0:
+ port->data[0] = b;
+ port->state = STATE_SYSEX_1;
+ break;
+ case STATE_SYSEX_1:
+ port->data[1] = b;
+ port->state = STATE_SYSEX_2;
+ break;
+ case STATE_SYSEX_2:
+ gmidi_transmit_packet(req,
+ p0 | 0x04, port->data[0], port->data[1], b);
+ port->state = STATE_SYSEX_0;
+ break;
+ }
+ }
+}
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req)
+{
+ struct usb_ep* ep = dev->in_ep;
+ struct gmidi_in_port* port = &dev->in_port;
+
+ if (!ep) {
+ return;
+ }
+ if (!req) {
+ req = alloc_ep_req(ep, buflen);
+ }
+ if (!req) {
+ ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n");
+ return;
+ }
+ req->length = 0;
+ req->complete = gmidi_complete;
+
+ if (port->active) {
+ while (req->length + 3 < buflen) {
+ uint8_t b;
+ if (snd_rawmidi_transmit(dev->in_substream, &b, 1)
+ != 1)
+ {
+ port->active = 0;
+ break;
+ }
+ gmidi_transmit_byte(req, port, b);
+ }
+ }
+ if (req->length > 0) {
+ usb_ep_queue(ep, req, GFP_ATOMIC);
+ } else {
+ free_ep_req(ep, req);
+ }
+}
+
+static void gmidi_in_tasklet(unsigned long data)
+{
+ struct gmidi_device* dev = (struct gmidi_device*)data;
+
+ gmidi_transmit(dev, NULL);
+}
+
+static int gmidi_in_open(struct snd_rawmidi_substream *substream)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_in_open\n");
+ dev->in_substream = substream;
+ dev->in_port.state = STATE_UNKNOWN;
+ return 0;
+}
+
+static int gmidi_in_close(struct snd_rawmidi_substream *substream)
+{
+ VDBG(dev, "gmidi_in_close\n");
+ return 0;
+}
+
+static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_in_trigger %d\n", up);
+ dev->in_port.active = up;
+ if (up) {
+ tasklet_hi_schedule(&dev->tasklet);
+ }
+}
+
+static int gmidi_out_open(struct snd_rawmidi_substream *substream)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_out_open\n");
+ dev->out_substream = substream;
+ return 0;
+}
+
+static int gmidi_out_close(struct snd_rawmidi_substream *substream)
+{
+ VDBG(dev, "gmidi_out_close\n");
+ return 0;
+}
+
+static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_out_trigger %d\n", up);
+ if (up) {
+ set_bit(substream->number, &dev->out_triggered);
+ } else {
+ clear_bit(substream->number, &dev->out_triggered);
+ }
+}
+
+static struct snd_rawmidi_ops gmidi_in_ops = {
+ .open = gmidi_in_open,
+ .close = gmidi_in_close,
+ .trigger = gmidi_in_trigger,
+};
+
+static struct snd_rawmidi_ops gmidi_out_ops = {
+ .open = gmidi_out_open,
+ .close = gmidi_out_close,
+ .trigger = gmidi_out_trigger
+};
+
+/* register as a sound "card" */
+static int gmidi_register_card(struct gmidi_device *dev)
+{
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ int err;
+ int out_ports = 1;
+ int in_ports = 1;
+ static struct snd_device_ops ops = {
+ .dev_free = gmidi_snd_free,
+ };
+
+ card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (!card) {
+ ERROR(dev, "snd_card_new failed\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+ dev->card = card;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops);
+ if (err < 0) {
+ ERROR(dev, "snd_device_new failed: error %d\n", err);
+ goto fail;
+ }
+
+ strcpy(card->driver, longname);
+ strcpy(card->longname, longname);
+ strcpy(card->shortname, shortname);
+
+ /* Set up rawmidi */
+ dev->in_port.dev = dev;
+ dev->in_port.active = 0;
+ snd_component_add(card, "MIDI");
+ err = snd_rawmidi_new(card, "USB MIDI Gadget", 0,
+ out_ports, in_ports, &rmidi);
+ if (err < 0) {
+ ERROR(dev, "snd_rawmidi_new failed: error %d\n", err);
+ goto fail;
+ }
+ dev->rmidi = rmidi;
+ strcpy(rmidi->name, card->shortname);
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = dev;
+
+ /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
+ It's an upside-down world being a gadget. */
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
+
+ snd_card_set_dev(card, &dev->gadget->dev);
+
+ /* register it - we're ready to go */
+ err = snd_card_register(card);
+ if (err < 0) {
+ ERROR(dev, "snd_card_register failed\n");
+ goto fail;
+ }
+
+ VDBG(dev, "gmidi_register_card finished ok\n");
+ return 0;
+
+fail:
+ if (dev->card) {
+ snd_card_free(dev->card);
+ dev->card = NULL;
+ }
+ return err;
+}
+
+/*
+ * Creates an output endpoint, and initializes output ports.
+ */
+static int __devinit gmidi_bind(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev;
+ struct usb_ep *in_ep, *out_ep;
+ int gcnum, err = 0;
+
+ /* support optional vendor/distro customization */
+ if (idVendor) {
+ if (!idProduct) {
+ printk(KERN_ERR "idVendor needs idProduct!\n");
+ return -ENODEV;
+ }
+ device_desc.idVendor = cpu_to_le16(idVendor);
+ device_desc.idProduct = cpu_to_le16(idProduct);
+ if (bcdDevice) {
+ device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+ }
+ }
+ if (iManufacturer) {
+ strlcpy(manufacturer, iManufacturer, sizeof(manufacturer));
+ } else {
+ snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
+ system_utsname.sysname, system_utsname.release,
+ gadget->name);
+ }
+ if (iProduct) {
+ strlcpy(product_desc, iProduct, sizeof(product_desc));
+ }
+ if (iSerialNumber) {
+ device_desc.iSerialNumber = STRING_SERIAL,
+ strlcpy(serial_number, iSerialNumber, sizeof(serial_number));
+ }
+
+ /* Bulk-only drivers like this one SHOULD be able to
+ * autoconfigure on any sane usb controller driver,
+ * but there may also be important quirks to address.
+ */
+ usb_ep_autoconfig_reset(gadget);
+ in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc);
+ if (!in_ep) {
+autoconf_fail:
+ printk(KERN_ERR "%s: can't autoconfigure on %s\n",
+ shortname, gadget->name);
+ return -ENODEV;
+ }
+ EP_IN_NAME = in_ep->name;
+ in_ep->driver_data = in_ep; /* claim */
+
+ out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc);
+ if (!out_ep) {
+ goto autoconf_fail;
+ }
+ EP_OUT_NAME = out_ep->name;
+ out_ep->driver_data = out_ep; /* claim */
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0) {
+ device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+ } else {
+ /* gmidi is so simple (no altsettings) that
+ * it SHOULD NOT have problems with bulk-capable hardware.
+ * so warn about unrecognized controllers, don't panic.
+ */
+ printk(KERN_WARNING "%s: controller '%s' not recognized\n",
+ shortname, gadget->name);
+ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+ }
+
+
+ /* ok, we made sense of the hardware ... */
+ dev = kzalloc(sizeof(*dev), SLAB_KERNEL);
+ if (!dev) {
+ return -ENOMEM;
+ }
+ spin_lock_init(&dev->lock);
+ dev->gadget = gadget;
+ dev->in_ep = in_ep;
+ dev->out_ep = out_ep;
+ set_gadget_data(gadget, dev);
+ tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev);
+
+ /* preallocate control response and buffer */
+ dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!dev->req) {
+ err = -ENOMEM;
+ goto fail;
+ }
+ dev->req->buf = usb_ep_alloc_buffer(gadget->ep0, USB_BUFSIZ,
+ &dev->req->dma, GFP_KERNEL);
+ if (!dev->req->buf) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ dev->req->complete = gmidi_setup_complete;
+
+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+ gadget->ep0->driver_data = dev;
+
+ INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
+ INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
+ EP_OUT_NAME, EP_IN_NAME);
+
+ /* register as an ALSA sound card */
+ err = gmidi_register_card(dev);
+ if (err < 0) {
+ goto fail;
+ }
+
+ VDBG(dev, "gmidi_bind finished ok\n");
+ return 0;
+
+fail:
+ gmidi_unbind(gadget);
+ return err;
+}
+
+
+static void gmidi_suspend(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+
+ if (gadget->speed == USB_SPEED_UNKNOWN) {
+ return;
+ }
+
+ DBG(dev, "suspend\n");
+}
+
+static void gmidi_resume(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+
+ DBG(dev, "resume\n");
+}
+
+
+static struct usb_gadget_driver gmidi_driver = {
+ .speed = USB_SPEED_FULL,
+ .function = (char *)longname,
+ .bind = gmidi_bind,
+ .unbind = __exit_p(gmidi_unbind),
+
+ .setup = gmidi_setup,
+ .disconnect = gmidi_disconnect,
+
+ .suspend = gmidi_suspend,
+ .resume = gmidi_resume,
+
+ .driver = {
+ .name = (char *)shortname,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gmidi_init(void)
+{
+ return usb_gadget_register_driver(&gmidi_driver);
+}
+module_init(gmidi_init);
+
+static void __exit gmidi_cleanup(void)
+{
+ usb_gadget_unregister_driver(&gmidi_driver);
+}
+module_exit(gmidi_cleanup);
+
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 3bdc5e3ba23..4655522a08d 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -32,6 +32,7 @@
#include <linux/compiler.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
+#include <linux/poll.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -222,7 +223,6 @@ static void put_ep (struct ep_data *data)
/* needs no more cleanup */
BUG_ON (!list_empty (&data->epfiles));
BUG_ON (waitqueue_active (&data->wait));
- BUG_ON (down_trylock (&data->lock) != 0);
kfree (data);
}
@@ -477,6 +477,10 @@ static int
ep_release (struct inode *inode, struct file *fd)
{
struct ep_data *data = fd->private_data;
+ int value;
+
+ if ((value = down_interruptible(&data->lock)) < 0)
+ return value;
/* clean up if this can be reopened */
if (data->state != STATE_EP_UNBOUND) {
@@ -485,6 +489,7 @@ ep_release (struct inode *inode, struct file *fd)
data->hs_desc.bDescriptorType = 0;
usb_ep_disable(data->ep);
}
+ up (&data->lock);
put_ep (data);
return 0;
}
@@ -709,7 +714,7 @@ ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
/*----------------------------------------------------------------------*/
/* used after endpoint configuration */
-static struct file_operations ep_io_operations = {
+static const struct file_operations ep_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -741,7 +746,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
struct ep_data *data = fd->private_data;
struct usb_ep *ep;
u32 tag;
- int value;
+ int value, length = len;
if ((value = down_interruptible (&data->lock)) < 0)
return value;
@@ -792,7 +797,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
goto fail0;
}
}
- value = len;
spin_lock_irq (&data->dev->lock);
if (data->dev->state == STATE_DEV_UNBOUND) {
@@ -822,8 +826,10 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
data->name);
data->state = STATE_EP_DEFER_ENABLE;
}
- if (value == 0)
+ if (value == 0) {
fd->f_op = &ep_io_operations;
+ value = length;
+ }
gone:
spin_unlock_irq (&data->dev->lock);
if (value < 0) {
@@ -844,7 +850,7 @@ fail1:
static int
ep_open (struct inode *inode, struct file *fd)
{
- struct ep_data *data = inode->u.generic_ip;
+ struct ep_data *data = inode->i_private;
int value = -EBUSY;
if (down_interruptible (&data->lock) != 0)
@@ -867,7 +873,7 @@ ep_open (struct inode *inode, struct file *fd)
}
/* used before endpoint configuration */
-static struct file_operations ep_config_operations = {
+static const struct file_operations ep_config_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -1009,7 +1015,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
else {
len = min (len, (size_t)dev->req->actual);
// FIXME don't call this with the spinlock held ...
- if (copy_to_user (buf, &dev->req->buf, len))
+ if (copy_to_user (buf, dev->req->buf, len))
retval = -EFAULT;
clean_req (dev->gadget->ep0, dev->req);
/* NOTE userspace can't yet choose to stall */
@@ -1229,6 +1235,35 @@ dev_release (struct inode *inode, struct file *fd)
return 0;
}
+static unsigned int
+ep0_poll (struct file *fd, poll_table *wait)
+{
+ struct dev_data *dev = fd->private_data;
+ int mask = 0;
+
+ poll_wait(fd, &dev->wait, wait);
+
+ spin_lock_irq (&dev->lock);
+
+ /* report fd mode change before acting on it */
+ if (dev->setup_abort) {
+ dev->setup_abort = 0;
+ mask = POLLHUP;
+ goto out;
+ }
+
+ if (dev->state == STATE_SETUP) {
+ if (dev->setup_in || dev->setup_can_stall)
+ mask = POLLOUT;
+ } else {
+ if (dev->ev_next != 0)
+ mask = POLLIN;
+ }
+out:
+ spin_unlock_irq(&dev->lock);
+ return mask;
+}
+
static int dev_ioctl (struct inode *inode, struct file *fd,
unsigned code, unsigned long value)
{
@@ -1241,14 +1276,14 @@ static int dev_ioctl (struct inode *inode, struct file *fd,
}
/* used after device configuration */
-static struct file_operations ep0_io_operations = {
+static const struct file_operations ep0_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = ep0_read,
.write = ep0_write,
.fasync = ep0_fasync,
- // .poll = ep0_poll,
+ .poll = ep0_poll,
.ioctl = dev_ioctl,
.release = dev_release,
};
@@ -1696,16 +1731,17 @@ gadgetfs_disconnect (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
+ spin_lock (&dev->lock);
if (dev->state == STATE_UNCONNECTED) {
DBG (dev, "already unconnected\n");
- return;
+ goto exit;
}
dev->state = STATE_UNCONNECTED;
INFO (dev, "disconnected\n");
- spin_lock (&dev->lock);
next_event (dev, GADGETFS_DISCONNECT);
ep0_readable (dev);
+exit:
spin_unlock (&dev->lock);
}
@@ -1909,7 +1945,7 @@ fail:
static int
dev_open (struct inode *inode, struct file *fd)
{
- struct dev_data *dev = inode->u.generic_ip;
+ struct dev_data *dev = inode->i_private;
int value = -EBUSY;
if (dev->state == STATE_DEV_DISABLED) {
@@ -1922,7 +1958,7 @@ dev_open (struct inode *inode, struct file *fd)
return value;
}
-static struct file_operations dev_init_operations = {
+static const struct file_operations dev_init_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -1966,11 +2002,10 @@ gadgetfs_make_inode (struct super_block *sb,
inode->i_mode = mode;
inode->i_uid = default_uid;
inode->i_gid = default_gid;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime
= CURRENT_TIME;
- inode->u.generic_ip = data;
+ inode->i_private = data;
inode->i_fop = fops;
}
return inode;
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index 09243239d94..3bda37f9a35 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -2,7 +2,7 @@
* Driver for the PLX NET2280 USB device controller.
* Specs and errata are available from <http://www.plxtech.com>.
*
- * PLX Technology Inc. (formerly NetChip Technology) supported the
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
* development of this driver.
*
*
@@ -26,7 +26,8 @@
* Copyright (C) 2003 David Brownell
* Copyright (C) 2003-2005 PLX Technology, Inc.
*
- * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility with 2282 chip
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ * with 2282 chip
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -85,7 +86,7 @@ static const char driver_name [] = "net2280";
static const char driver_desc [] = DRIVER_DESC;
static const char ep0name [] = "ep0";
-static const char *ep_name [] = {
+static const char *const ep_name [] = {
ep0name,
"ep-a", "ep-b", "ep-c", "ep-d",
"ep-e", "ep-f",
@@ -225,7 +226,9 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
if (!ep->is_in)
writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
else if (dev->pdev->device != 0x2280) {
- /* Added for 2282, Don't use nak packets on an in endpoint, this was ignored on 2280 */
+ /* Added for 2282, Don't use nak packets on an in endpoint,
+ * this was ignored on 2280
+ */
writel ((1 << CLEAR_NAK_OUT_PACKETS)
| (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
}
@@ -288,7 +291,7 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
return -ETIMEDOUT;
}
-static struct usb_ep_ops net2280_ep_ops;
+static const struct usb_ep_ops net2280_ep_ops;
static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
{
@@ -449,34 +452,15 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
/*-------------------------------------------------------------------------*/
-#undef USE_KMALLOC
-
-/* many common platforms have dma-coherent caches, which means that it's
- * safe to use kmalloc() memory for all i/o buffers without using any
- * cache flushing calls. (unless you're trying to share cache lines
- * between dma and non-dma activities, which is a slow idea in any case.)
+/*
+ * dma-coherent memory allocation (for dma-capable endpoints)
*
- * other platforms need more care, with 2.5 having a moderately general
- * solution (which falls down for allocations smaller than one page)
- * that improves significantly on the 2.4 PCI allocators by removing
- * the restriction that memory never be freed in_interrupt().
+ * NOTE: the dma_*_coherent() API calls suck. Most implementations are
+ * (a) page-oriented, so small buffers lose big; and (b) asymmetric with
+ * respect to calls with irqs disabled: alloc is safe, free is not.
+ * We currently work around (b), but not (a).
*/
-#if defined(CONFIG_X86)
-#define USE_KMALLOC
-
-#elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
-#define USE_KMALLOC
-#elif defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
-#define USE_KMALLOC
-
-/* FIXME there are other cases, including an x86-64 one ... */
-#endif
-
-/* allocating buffers this way eliminates dma mapping overhead, which
- * on some platforms will mean eliminating a per-io buffer copy. with
- * some kinds of system caches, further tweaks may still be needed.
- */
static void *
net2280_alloc_buffer (
struct usb_ep *_ep,
@@ -493,43 +477,71 @@ net2280_alloc_buffer (
return NULL;
*dma = DMA_ADDR_INVALID;
-#if defined(USE_KMALLOC)
- retval = kmalloc(bytes, gfp_flags);
- if (retval)
- *dma = virt_to_phys(retval);
-#else
- if (ep->dma) {
- /* the main problem with this call is that it wastes memory
- * on typical 1/N page allocations: it allocates 1-N pages.
- */
-#warning Using dma_alloc_coherent even with buffers smaller than a page.
+ if (ep->dma)
retval = dma_alloc_coherent(&ep->dev->pdev->dev,
bytes, dma, gfp_flags);
- } else
+ else
retval = kmalloc(bytes, gfp_flags);
-#endif
return retval;
}
+static DEFINE_SPINLOCK(buflock);
+static LIST_HEAD(buffers);
+
+struct free_record {
+ struct list_head list;
+ struct device *dev;
+ unsigned bytes;
+ dma_addr_t dma;
+};
+
+static void do_free(unsigned long ignored)
+{
+ spin_lock_irq(&buflock);
+ while (!list_empty(&buffers)) {
+ struct free_record *buf;
+
+ buf = list_entry(buffers.next, struct free_record, list);
+ list_del(&buf->list);
+ spin_unlock_irq(&buflock);
+
+ dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
+
+ spin_lock_irq(&buflock);
+ }
+ spin_unlock_irq(&buflock);
+}
+
+static DECLARE_TASKLET(deferred_free, do_free, 0);
+
static void
net2280_free_buffer (
struct usb_ep *_ep,
- void *buf,
+ void *address,
dma_addr_t dma,
unsigned bytes
) {
/* free memory into the right allocator */
-#ifndef USE_KMALLOC
if (dma != DMA_ADDR_INVALID) {
struct net2280_ep *ep;
+ struct free_record *buf = address;
+ unsigned long flags;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep)
return;
- dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
+
+ ep = container_of (_ep, struct net2280_ep, ep);
+ buf->dev = &ep->dev->pdev->dev;
+ buf->bytes = bytes;
+ buf->dma = dma;
+
+ spin_lock_irqsave(&buflock, flags);
+ list_add_tail(&buf->list, &buffers);
+ tasklet_schedule(&deferred_free);
+ spin_unlock_irqrestore(&buflock, flags);
} else
-#endif
- kfree (buf);
+ kfree (address);
}
/*-------------------------------------------------------------------------*/
@@ -737,7 +749,8 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
*/
if (ep->is_in)
dmacount |= (1 << DMA_DIRECTION);
- if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || ep->dev->pdev->device != 0x2280)
+ if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)
+ || ep->dev->pdev->device != 0x2280)
dmacount |= (1 << END_OF_CHAIN);
req->valid = valid;
@@ -812,7 +825,7 @@ static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
/* previous OUT packet might have been short */
if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
- & (1 << NAK_OUT_PACKETS)) != 0) {
+ & (1 << NAK_OUT_PACKETS)) != 0) {
writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
&ep->regs->ep_stat);
@@ -1373,7 +1386,7 @@ net2280_fifo_flush (struct usb_ep *_ep)
(void) readl (&ep->regs->ep_rsp);
}
-static struct usb_ep_ops net2280_ep_ops = {
+static const struct usb_ep_ops net2280_ep_ops = {
.enable = net2280_enable,
.disable = net2280_disable,
@@ -1631,7 +1644,7 @@ show_registers (struct device *_dev, struct device_attribute *attr, char *buf)
}
/* Indexed Registers */
- // none yet
+ // none yet
/* Statistics */
t = scnprintf (next, size, "\nirqs: ");
@@ -1691,11 +1704,11 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
({ char *val;
switch (d->bmAttributes & 0x03) {
case USB_ENDPOINT_XFER_BULK:
- val = "bulk"; break;
+ val = "bulk"; break;
case USB_ENDPOINT_XFER_INT:
- val = "intr"; break;
+ val = "intr"; break;
default:
- val = "iso"; break;
+ val = "iso"; break;
}; val; }),
le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
ep->dma ? "dma" : "pio", ep->fifo_size
@@ -1808,8 +1821,8 @@ extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
* net2280_set_fifo_mode - change allocation of fifo buffers
* @gadget: access to the net2280 device that will be updated
* @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
- * 1 for two 2kB buffers (ep-a and ep-b only);
- * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
+ * 1 for two 2kB buffers (ep-a and ep-b only);
+ * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
*
* returns zero on success, else negative errno. when this succeeds,
* the contents of gadget->ep_list may have changed.
@@ -2241,7 +2254,8 @@ static void handle_ep_small (struct net2280_ep *ep)
req->td->dmacount = 0;
t = readl (&ep->regs->ep_avail);
dma_done (ep, req, count,
- (ep->out_overflow || t) ? -EOVERFLOW : 0);
+ (ep->out_overflow || t)
+ ? -EOVERFLOW : 0);
}
/* also flush to prevent erratum 0106 trouble */
@@ -2411,7 +2425,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
, &ep->regs->ep_stat);
u.raw [0] = readl (&dev->usb->setup0123);
u.raw [1] = readl (&dev->usb->setup4567);
-
+
cpu_to_le32s (&u.raw [0]);
cpu_to_le32s (&u.raw [1]);
@@ -2578,14 +2592,16 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
/* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
* Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and
- * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
+ * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
* only indicates a change in the reset state).
*/
if (stat & tmp) {
writel (tmp, &dev->regs->irqstat1);
- if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
- ((readl (&dev->usb->usbstat) & mask) == 0))
- || ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0)
+ if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
+ && ((readl (&dev->usb->usbstat) & mask)
+ == 0))
+ || ((readl (&dev->usb->usbctl)
+ & (1 << VBUS_PIN)) == 0)
) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
DEBUG (dev, "disconnect %s\n",
dev->driver->driver.name);
@@ -2852,7 +2868,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
/* now all the pci goodies ... */
if (pci_enable_device (pdev) < 0) {
- retval = -ENODEV;
+ retval = -ENODEV;
goto done;
}
dev->enabled = 1;
@@ -2870,6 +2886,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
}
dev->region = 1;
+ /* FIXME provide firmware download interface to put
+ * 8051 code into the chip, e.g. to turn on PCI PM.
+ */
+
base = ioremap_nocache (resource, len);
if (base == NULL) {
DEBUG (dev, "can't map memory\n");
@@ -2984,16 +3004,16 @@ static void net2280_shutdown (struct pci_dev *pdev)
/*-------------------------------------------------------------------------*/
-static struct pci_device_id pci_ids [] = { {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
+static const struct pci_device_id pci_ids [] = { {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
.vendor = 0x17cc,
.device = 0x2280,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
}, {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
.vendor = 0x17cc,
.device = 0x2282,
.subvendor = PCI_ANY_ID,
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 2de9748ee67..0a64504c254 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -40,7 +40,7 @@
#include <linux/platform_device.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
@@ -2437,7 +2437,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
return single_open(file, proc_udc_show, NULL);
}
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
.open = proc_udc_open,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index fff027d30a0..f1adcf8b202 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -150,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
static void nuke (struct pxa2xx_ep *, int status);
+/* one GPIO should be used to detect VBUS from the host */
+static int is_vbus_present(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->gpio_vbus)
+ return pxa_gpio_get(mach->gpio_vbus);
+ if (mach->udc_is_connected)
+ return mach->udc_is_connected();
+ return 1;
+}
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->gpio_pullup)
+ pxa_gpio_set(mach->gpio_pullup, 0);
+ else if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->gpio_pullup)
+ pxa_gpio_set(mach->gpio_pullup, 1);
+ else if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
static void pio_irq_enable(int bEndpointAddress)
{
bEndpointAddress &= 0xf;
@@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
#endif
+static irqreturn_t
+udc_vbus_irq(int irq, void *_dev, struct pt_regs *r)
+{
+ struct pxa2xx_udc *dev = _dev;
+ int vbus = pxa_gpio_get(dev->mach->gpio_vbus);
+
+ pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+ return IRQ_HANDLED;
+}
+
/*-------------------------------------------------------------------------*/
@@ -2438,7 +2481,7 @@ static struct pxa2xx_udc memory = {
static int __init pxa2xx_udc_probe(struct platform_device *pdev)
{
struct pxa2xx_udc *dev = &memory;
- int retval, out_dma = 1;
+ int retval, out_dma = 1, vbus_irq;
u32 chiprev;
/* insist on Intel/ARM/XScale */
@@ -2502,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
/* other non-static parts of init */
dev->dev = &pdev->dev;
dev->mach = pdev->dev.platform_data;
+ if (dev->mach->gpio_vbus) {
+ vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
+ pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
+ | GPIO_IN);
+ set_irq_type(vbus_irq, IRQT_BOTHEDGE);
+ } else
+ vbus_irq = 0;
+ if (dev->mach->gpio_pullup)
+ pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
+ | GPIO_OUT | GPIO_DFLT_LOW);
init_timer(&dev->timer);
dev->timer.function = udc_watchdog;
@@ -2557,8 +2610,19 @@ lubbock_fail0:
HEX_DISPLAY(dev->stats.irqs);
LUB_DISC_BLNK_LED &= 0xff;
#endif
- }
+ } else
#endif
+ if (vbus_irq) {
+ retval = request_irq(vbus_irq, udc_vbus_irq,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM,
+ driver_name, dev);
+ if (retval != 0) {
+ printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+ driver_name, vbus_irq, retval);
+ free_irq(IRQ_USB, dev);
+ return -EBUSY;
+ }
+ }
create_proc_files();
return 0;
@@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
free_irq(LUBBOCK_USB_IRQ, dev);
}
#endif
+ if (dev->mach->gpio_vbus)
+ free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
platform_set_drvdata(pdev, NULL);
the_controller = NULL;
return 0;
diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
index 19a883f7d1b..8e598c8bf4e 100644
--- a/drivers/usb/gadget/pxa2xx_udc.h
+++ b/drivers/usb/gadget/pxa2xx_udc.h
@@ -177,27 +177,19 @@ struct pxa2xx_udc {
static struct pxa2xx_udc *the_controller;
-/* one GPIO should be used to detect VBUS from the host */
-static inline int is_vbus_present(void)
+static inline int pxa_gpio_get(unsigned gpio)
{
- if (!the_controller->mach->udc_is_connected)
- return 1;
- return the_controller->mach->udc_is_connected();
+ return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
}
-/* one GPIO should control a D+ pullup, so host sees this device (or not) */
-static inline void pullup_off(void)
+static inline void pxa_gpio_set(unsigned gpio, int is_on)
{
- if (!the_controller->mach->udc_command)
- return;
- the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
-}
+ int mask = GPIO_bit(gpio);
-static inline void pullup_on(void)
-{
- if (!the_controller->mach->udc_command)
- return;
- the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+ if (is_on)
+ GPSR(gpio) = mask;
+ else
+ GPCR(gpio) = mask;
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index e762aa19ab0..b893e3118e1 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -1120,12 +1120,15 @@ static int gs_send(struct gs_dev *dev)
gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2));
list_del(&req_entry->re_entry);
req->length = len;
+ spin_unlock_irqrestore(&dev->dev_lock, flags);
if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
printk(KERN_ERR
"gs_send: cannot queue read request, ret=%d\n",
ret);
+ spin_lock_irqsave(&dev->dev_lock, flags);
break;
}
+ spin_lock_irqsave(&dev->dev_lock, flags);
} else {
break;
}
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index b93d71d28db..cf10cbc98f8 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -83,6 +83,7 @@ config USB_OHCI_HCD
tristate "OHCI HCD support"
depends on USB && USB_ARCH_HAS_OHCI
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+ select I2C if ARCH_PNX4008
---help---
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
@@ -141,6 +142,34 @@ config USB_UHCI_HCD
To compile this driver as a module, choose M here: the
module will be called uhci-hcd.
+config USB_U132_HCD
+ tristate "Elan U132 Adapter Host Controller"
+ depends on USB && USB_FTDI_ELAN
+ default M
+ help
+ The U132 adapter is a USB to CardBus adapter specifically designed
+ for PC cards that contain an OHCI host controller. Typical PC cards
+ are the Orange Mobile 3G Option GlobeTrotter Fusion card. The U132
+ adapter will *NOT* work with PC cards that do not contain an OHCI
+ controller.
+
+ For those PC cards that contain multiple OHCI controllers only ther
+ first one is used.
+
+ The driver consists of two modules, the "ftdi-elan" module is a
+ USB client driver that interfaces to the FTDI chip within ELAN's
+ USB-to-PCMCIA adapter, and this "u132-hcd" module is a USB host
+ controller driver that talks to the OHCI controller within the
+ CardBus cards that are inserted in the U132 adapter.
+
+ This driver has been tested with a CardBus OHCI USB adapter, and
+ worked with a USB PEN Drive inserted into the first USB port of
+ the PCCARD. A rather pointless thing to do, but useful for testing.
+
+ It is safe to say M here.
+
+ See also <http://www.elandigitalsystems.com/support/ufaq/u132linux.php>
+
config USB_SL811_HCD
tristate "SL811HS HCD support"
depends on USB
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index e3020f4b17b..a2e58c86849 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -14,4 +14,5 @@ obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
+obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index 26ed757d22a..5d1b12aad77 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -200,6 +200,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
.reset = ehci_init,
.start = ehci_run,
.stop = ehci_stop,
+ .shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -268,6 +269,7 @@ MODULE_ALIAS("au1xxx-ehci");
static struct platform_driver ehci_hcd_au1xxx_driver = {
.probe = ehci_hcd_au1xxx_drv_probe,
.remove = ehci_hcd_au1xxx_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ehci_hcd_au1xxx_drv_suspend, */
/*.resume = ehci_hcd_au1xxx_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 65ac9fef3a7..23b95b2bfe1 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2001-2002 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -65,7 +65,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
for (i = 0; i < HCS_N_PORTS (params); i++) {
// FIXME MIPS won't readb() ...
byte = readb (&ehci->caps->portroute[(i>>1)]);
- sprintf(tmp, "%d ",
+ sprintf(tmp, "%d ",
((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
strcat(buf, tmp);
}
@@ -141,12 +141,12 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
}
static void __attribute__((__unused__))
-dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
+dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
{
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
ehci_dbg (ehci,
- " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_transaction[0]),
le32_to_cpu(itd->hw_transaction[1]),
le32_to_cpu(itd->hw_transaction[2]),
@@ -156,7 +156,7 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
le32_to_cpu(itd->hw_transaction[6]),
le32_to_cpu(itd->hw_transaction[7]));
ehci_dbg (ehci,
- " buf: %08x %08x %08x %08x %08x %08x %08x\n",
+ " buf: %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_bufp[0]),
le32_to_cpu(itd->hw_bufp[1]),
le32_to_cpu(itd->hw_bufp[2]),
@@ -171,12 +171,12 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
}
static void __attribute__((__unused__))
-dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
+dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
ehci_dbg (ehci,
- " addr %08x sched %04x result %08x buf %08x %08x\n",
+ " addr %08x sched %04x result %08x buf %08x %08x\n",
le32_to_cpu(sitd->hw_fullspeed_ep),
le32_to_cpu(sitd->hw_uframe),
le32_to_cpu(sitd->hw_results),
@@ -451,7 +451,7 @@ show_async (struct class_device *class_dev, char *buf)
*buf = 0;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
@@ -497,7 +497,7 @@ show_periodic (struct class_device *class_dev, char *buf)
seen_count = 0;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
@@ -634,7 +634,7 @@ show_registers (struct class_device *class_dev, char *buf)
static char label [] = "";
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
@@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf)
}
if (ehci->reclaim) {
- temp = scnprintf (next, size, "reclaim qh %p%s\n",
- ehci->reclaim,
- ehci->reclaim_ready ? " ready" : "");
+ temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
size -= temp;
next += temp;
}
@@ -785,10 +783,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ehci_hcd *ehci)
{
struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+ int retval;
- class_device_create_file(cldev, &class_device_attr_async);
- class_device_create_file(cldev, &class_device_attr_periodic);
- class_device_create_file(cldev, &class_device_attr_registers);
+ retval = class_device_create_file(cldev, &class_device_attr_async);
+ retval = class_device_create_file(cldev, &class_device_attr_periodic);
+ retval = class_device_create_file(cldev, &class_device_attr_registers);
}
static inline void remove_debug_files (struct ehci_hcd *ehci)
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index d030516edfb..1a915e982c1 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -285,6 +285,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
.resume = ehci_bus_resume,
#endif
.stop = ehci_stop,
+ .shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -329,6 +330,7 @@ MODULE_ALIAS("fsl-ehci");
static struct platform_driver ehci_fsl_driver = {
.probe = ehci_fsl_drv_probe,
.remove = ehci_fsl_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "fsl-ehci",
},
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index d63177a8eae..5ac91859113 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2000-2004 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -70,7 +70,7 @@
* 2002-08-06 Handling for bulk and interrupt transfers is mostly shared;
* only scheduling is different, no arbitrary limitations.
* 2002-07-25 Sanity check PCI reads, mostly for better cardbus support,
- * clean up HC run state handshaking.
+ * clean up HC run state handshaking.
* 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts
* 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other
* missing pieces: enabling 64bit dma, handoff from BIOS/SMM.
@@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
-#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
+#define EHCI_IAA_MSECS 10 /* arbitrary */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
@@ -254,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
#include "ehci-hub.c"
@@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
/*-------------------------------------------------------------------------*/
-static void ehci_watchdog (unsigned long param)
+static void ehci_iaa_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
+ u32 status;
spin_lock_irqsave (&ehci->lock, flags);
+ WARN_ON(!ehci->reclaim);
- /* lost IAA irqs wedge things badly; seen with a vt8235 */
+ /* lost IAA irqs wedge things badly; seen first with a vt8235 */
if (ehci->reclaim) {
- u32 status = readl (&ehci->regs->status);
-
+ status = readl (&ehci->regs->status);
if (status & STS_IAA) {
ehci_vdbg (ehci, "lost IAA\n");
COUNT (ehci->stats.lost_iaa);
writel (STS_IAA, &ehci->regs->status);
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, NULL);
}
}
- /* stop async processing after it's idled a bit */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+static void ehci_watchdog (unsigned long param)
+{
+ struct ehci_hcd *ehci = (struct ehci_hcd *) param;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* stop async processing after it's idled a bit */
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
- start_unlink_async (ehci, ehci->async);
+ start_unlink_async (ehci, ehci->async);
/* ehci could run by timer, without IRQs ... */
ehci_work (ehci, NULL);
@@ -292,21 +304,20 @@ static void ehci_watchdog (unsigned long param)
spin_unlock_irqrestore (&ehci->lock, flags);
}
-/* Reboot notifiers kick in for silicon on any bus (not just pci, etc).
+/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
* This forcibly disables dma and IRQs, helping kexec and other cases
* where the next system software may expect clean state.
*/
-static int
-ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+static void
+ehci_shutdown (struct usb_hcd *hcd)
{
- struct ehci_hcd *ehci;
+ struct ehci_hcd *ehci;
- ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+ ehci = hcd_to_ehci (hcd);
(void) ehci_halt (ehci);
/* make BIOS/etc use companion controller during reboot */
writel (0, &ehci->regs->configured_flag);
- return 0;
}
static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -334,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
{
timer_action_done (ehci, TIMER_IO_WATCHDOG);
- if (ehci->reclaim_ready)
- end_unlink_async (ehci, regs);
/* another CPU may drop ehci->lock during a schedule scan while
* it reports urb completions. this flag guards against bogus
@@ -370,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd)
/* no more interrupts ... */
del_timer_sync (&ehci->watchdog);
+ del_timer_sync (&ehci->iaa_watchdog);
spin_lock_irq(&ehci->lock);
if (HC_IS_RUNNING (hcd->state))
@@ -381,7 +391,6 @@ static void ehci_stop (struct usb_hcd *hcd)
/* let companion controllers work when we aren't */
writel (0, &ehci->regs->configured_flag);
- unregister_reboot_notifier (&ehci->reboot_notifier);
remove_debug_files (ehci);
@@ -417,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd)
ehci->watchdog.function = ehci_watchdog;
ehci->watchdog.data = (unsigned long) ehci;
+ init_timer(&ehci->iaa_watchdog);
+ ehci->iaa_watchdog.function = ehci_iaa_watchdog;
+ ehci->iaa_watchdog.data = (unsigned long) ehci;
+
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -427,13 +440,12 @@ static int ehci_init(struct usb_hcd *hcd)
/* controllers may cache some of the periodic schedule ... */
hcc_params = readl(&ehci->caps->hcc_params);
- if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
+ if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
ehci->i_thresh = 8;
else // N microframes cached
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
ehci->reclaim = NULL;
- ehci->reclaim_ready = 0;
ehci->next_uframe = -1;
/*
@@ -483,9 +495,6 @@ static int ehci_init(struct usb_hcd *hcd)
}
ehci->command = temp;
- ehci->reboot_notifier.notifier_call = ehci_reboot;
- register_reboot_notifier(&ehci->reboot_notifier);
-
return 0;
}
@@ -499,7 +508,6 @@ static int ehci_run (struct usb_hcd *hcd)
/* EHCI spec section 4.1 */
if ((retval = ehci_reset(ehci)) != 0) {
- unregister_reboot_notifier(&ehci->reboot_notifier);
ehci_mem_cleanup(ehci);
return retval;
}
@@ -611,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
/* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) {
COUNT (ehci->stats.reclaim);
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, regs);
bh = 1;
}
@@ -715,10 +723,14 @@ static int ehci_urb_enqueue (
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- /* if we need to use IAA and it's busy, defer */
- if (qh->qh_state == QH_STATE_LINKED
- && ehci->reclaim
- && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
+ // BUG_ON(qh->qh_state != QH_STATE_LINKED);
+
+ /* failfast */
+ if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ end_unlink_async (ehci, NULL);
+
+ /* defer till later if busy */
+ else if (ehci->reclaim) {
struct ehci_qh *last;
for (last = ehci->reclaim;
@@ -728,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->qh_state = QH_STATE_UNLINK_WAIT;
last->reclaim = qh;
- /* bypass IAA if the hc can't care */
- } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
- end_unlink_async (ehci, NULL);
-
- /* something else might have unlinked the qh by now */
- if (qh->qh_state == QH_STATE_LINKED)
+ /* start IAA cycle */
+ } else
start_unlink_async (ehci, qh);
}
@@ -755,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
- unlink_async (ehci, qh);
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ unlink_async (ehci, qh);
+ break;
+ case QH_STATE_UNLINK:
+ case QH_STATE_UNLINK_WAIT:
+ /* already started */
+ break;
+ case QH_STATE_IDLE:
+ WARN_ON(1);
+ break;
+ }
break;
case PIPE_INTERRUPT:
@@ -847,6 +867,7 @@ rescan:
unlink_async (ehci, qh);
/* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */
+ case QH_STATE_UNLINK_WAIT:
idle_timeout:
spin_unlock_irqrestore (&ehci->lock, flags);
schedule_timeout_uninterruptible(1);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index d03e3cad5ca..b2ee13c5851 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, NULL);
ehci_work(ehci, NULL);
/* suspend any active/unsuspended ports, maybe allow wakeup */
@@ -103,10 +103,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) {
- /* at least some APM implementations will try to deliver
+ /* at least some APM implementations will try to deliver
* IRQs right away, so delay them until we're ready.
- */
- intr_enable = 1;
+ */
+ intr_enable = 1;
writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
@@ -232,7 +232,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
buf [1] = 0;
retval++;
}
-
+
/* no hub change reports (bit 0) for now (power, ...) */
/* port N changes (bit N)? */
@@ -304,7 +304,7 @@ ehci_hub_descriptor (
/*-------------------------------------------------------------------------*/
-#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
static int ehci_hub_control (
struct usb_hcd *hcd,
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 766061e0260..a8ba2e1497a 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2001 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -25,7 +25,7 @@
* - data used only by the HCD ... kmalloc is fine
* - async and periodic schedules, shared by HC and HCD ... these
* need to use dma_pool or dma_alloc_coherent
- * - driver buffers, read/written by HC ... single shot DMA mapped
+ * - driver buffers, read/written by HC ... single shot DMA mapped
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pageable.
@@ -119,7 +119,7 @@ static inline void qh_put (struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
-/* The queue heads and transfer descriptors are managed from pools tied
+/* The queue heads and transfer descriptors are managed from pools tied
* to each of the "per device" structures.
* This is the initialisation and cleanup code.
*/
@@ -165,7 +165,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
int i;
/* QTDs for control/bulk/intr transfers */
- ehci->qtd_pool = dma_pool_create ("ehci_qtd",
+ ehci->qtd_pool = dma_pool_create ("ehci_qtd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qtd),
32 /* byte alignment (for hw parts) */,
@@ -175,7 +175,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
}
/* QHs for control/bulk/intr transfers */
- ehci->qh_pool = dma_pool_create ("ehci_qh",
+ ehci->qh_pool = dma_pool_create ("ehci_qh",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qh),
32 /* byte alignment (for hw parts) */,
@@ -189,7 +189,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
}
/* ITD for high speed ISO transfers */
- ehci->itd_pool = dma_pool_create ("ehci_itd",
+ ehci->itd_pool = dma_pool_create ("ehci_itd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_itd),
32 /* byte alignment (for hw parts) */,
@@ -199,7 +199,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
}
/* SITD for full/low speed split ISO transfers */
- ehci->sitd_pool = dma_pool_create ("ehci_sitd",
+ ehci->sitd_pool = dma_pool_create ("ehci_sitd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_sitd),
32 /* byte alignment (for hw parts) */,
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index cadffacd945..08d0472d4f5 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -238,6 +238,12 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
writel (0, &ehci->regs->intr_enable);
(void)readl(&ehci->regs->intr_enable);
+ /* make sure snapshot being resumed re-enumerates everything */
+ if (message.event == PM_EVENT_PRETHAW) {
+ ehci_halt(ehci);
+ ehci_reset(ehci);
+ }
+
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail:
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -297,7 +303,7 @@ restart:
/* emptying the schedule aborts any urbs */
spin_lock_irq(&ehci->lock);
if (ehci->reclaim)
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, NULL);
ehci_work(ehci, NULL);
spin_unlock_irq(&ehci->lock);
@@ -332,6 +338,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
.resume = ehci_pci_resume,
#endif
.stop = ehci_stop,
+ .shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -378,4 +385,5 @@ static struct pci_driver ehci_pci_driver = {
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif
+ .shutdown = usb_hcd_pci_shutdown,
};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index e469221e7ec..7fc25b6bd7d 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -31,7 +31,7 @@
* ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
* interrupts) needs careful scheduling. Performance improvements can be
* an ongoing challenge. That's in "ehci-sched.c".
- *
+ *
* USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
* or otherwise through transaction translators (TTs) in USB 2.0 hubs using
* (b) special fields in qh entries or (c) split iso entries. TTs will
@@ -199,7 +199,7 @@ static void qtd_copy_status (
&& ((token & QTD_STS_MMF) != 0
|| QTD_CERR(token) == 0)
&& (!ehci_is_TDI(ehci)
- || urb->dev->tt->hub !=
+ || urb->dev->tt->hub !=
ehci_to_hcd(ehci)->self.root_hub)) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
@@ -364,7 +364,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
*/
if (likely (urb->status == -EINPROGRESS))
continue;
-
+
/* issue status after short control reads */
if (unlikely (do_status != 0)
&& QTD_PID (token) == 0 /* OUT */) {
@@ -388,7 +388,7 @@ halt:
wmb ();
}
}
-
+
/* remove it from the queue */
spin_lock (&urb->lock);
qtd_copy_status (ehci, urb, qtd->length, token);
@@ -518,7 +518,7 @@ qh_urb_transaction (
/* for zero length DATA stages, STATUS is always IN */
if (len == 0)
token |= (1 /* "in" */ << 8);
- }
+ }
/*
* data transfer stage: buffer setup
@@ -759,7 +759,7 @@ qh_make (
}
break;
default:
- dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+ dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
done:
qh_put (qh);
return NULL;
@@ -967,17 +967,16 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
struct ehci_qh *qh = ehci->reclaim;
struct ehci_qh *next;
- timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+ iaa_watchdog_done (ehci);
// qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = NULL;
- qh_put (qh); // refcount from reclaim
+ qh_put (qh); // refcount from reclaim
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->reclaim;
ehci->reclaim = next;
- ehci->reclaim_ready = 0;
qh->reclaim = NULL;
qh_completions (ehci, qh, regs);
@@ -1031,7 +1030,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
timer_action_done (ehci, TIMER_ASYNC_OFF);
}
return;
- }
+ }
qh->qh_state = QH_STATE_UNLINK;
ehci->reclaim = qh = qh_get (qh);
@@ -1046,17 +1045,16 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) {
/* if (unlikely (qh->reclaim != 0))
- * this will recurse, probably not much
+ * this will recurse, probably not much
*/
end_unlink_async (ehci, NULL);
return;
}
- ehci->reclaim_ready = 0;
cmd |= CMD_IAAD;
writel (cmd, &ehci->regs->command);
(void) readl (&ehci->regs->command);
- timer_action (ehci, TIMER_IAA_WATCHDOG);
+ iaa_watchdog_start (ehci);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 4859900bd13..e5e9c653c90 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2001-2004 by David Brownell
* Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -613,7 +613,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
static int check_period (
- struct ehci_hcd *ehci,
+ struct ehci_hcd *ehci,
unsigned frame,
unsigned uframe,
unsigned period,
@@ -629,7 +629,7 @@ static int check_period (
/*
* 80% periodic == 100 usec/uframe available
- * convert "usecs we need" to "max already claimed"
+ * convert "usecs we need" to "max already claimed"
*/
usecs = 100 - usecs;
@@ -659,14 +659,14 @@ static int check_period (
}
static int check_intr_schedule (
- struct ehci_hcd *ehci,
+ struct ehci_hcd *ehci,
unsigned frame,
unsigned uframe,
const struct ehci_qh *qh,
__le32 *c_maskp
)
{
- int retval = -ENOSPC;
+ int retval = -ENOSPC;
u8 mask = 0;
if (qh->c_usecs && uframe >= 6) /* FSTN territory? */
@@ -701,7 +701,7 @@ static int check_intr_schedule (
/* Make sure this tt's buffer is also available for CSPLITs.
* We pessimize a bit; probably the typical full speed case
* doesn't need the second CSPLIT.
- *
+ *
* NOTE: both SPLIT and CSPLIT could be checked in just
* one smart pass...
*/
@@ -728,7 +728,7 @@ done:
*/
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- int status;
+ int status;
unsigned uframe;
__le32 c_mask;
unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
@@ -784,7 +784,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
/* stuff into the periodic schedule */
- status = qh_link_periodic (ehci, qh);
+ status = qh_link_periodic (ehci, qh);
done:
return status;
}
@@ -1681,7 +1681,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
status = -ESHUTDOWN;
else
status = iso_stream_schedule (ehci, urb, stream);
- if (likely (status == 0))
+ if (likely (status == 0))
itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -1738,7 +1738,7 @@ sitd_sched_init (
if (packet->buf1 != (buf & ~(u64)0x0fff))
packet->cross = 1;
- /* OUT uses multiple start-splits */
+ /* OUT uses multiple start-splits */
if (stream->bEndpointAddress & USB_DIR_IN)
continue;
length = (length + 187) / 188;
@@ -1925,7 +1925,7 @@ sitd_link_urb (
/*-------------------------------------------------------------------------*/
#define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
- | SITD_STS_XACT | SITD_STS_MMF)
+ | SITD_STS_XACT | SITD_STS_MMF)
static unsigned
sitd_complete (
@@ -2043,7 +2043,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
status = -ESHUTDOWN;
else
status = iso_stream_schedule (ehci, urb, stream);
- if (status == 0)
+ if (status == 0)
sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -2226,5 +2226,5 @@ restart:
now_uframe++;
now_uframe %= mod;
}
- }
+ }
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 679c1cdcc91..6aac39f50e0 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2001-2002 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -58,7 +58,6 @@ struct ehci_hcd { /* one per controller */
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
- unsigned reclaim_ready : 1;
unsigned scanning : 1;
/* periodic schedule support */
@@ -81,8 +80,8 @@ struct ehci_hcd { /* one per controller */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
+ struct timer_list iaa_watchdog;
struct timer_list watchdog;
- struct notifier_block reboot_notifier;
unsigned long actions;
unsigned stamp;
unsigned long next_statechange;
@@ -104,7 +103,7 @@ struct ehci_hcd { /* one per controller */
#endif
};
-/* convert between an HCD pointer and the corresponding EHCI_HCD */
+/* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
{
return (struct ehci_hcd *) (hcd->hcd_priv);
@@ -115,9 +114,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
}
+static inline void
+iaa_watchdog_start (struct ehci_hcd *ehci)
+{
+ WARN_ON(timer_pending(&ehci->iaa_watchdog));
+ mod_timer (&ehci->iaa_watchdog,
+ jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
+}
+
+static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
+{
+ del_timer (&ehci->iaa_watchdog);
+}
+
enum ehci_timer_action {
TIMER_IO_WATCHDOG,
- TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
@@ -135,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
unsigned long t;
switch (action) {
- case TIMER_IAA_WATCHDOG:
- t = EHCI_IAA_JIFFIES;
- break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
@@ -154,8 +162,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
- if (action != TIMER_IAA_WATCHDOG
- && t > ehci->watchdog.expires
+ if (time_before_eq(t, ehci->watchdog.expires)
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);
@@ -179,8 +186,8 @@ struct ehci_caps {
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
-#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
-#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
@@ -205,7 +212,7 @@ struct ehci_regs {
#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
#define CMD_ASE (1<<5) /* async schedule enable */
-#define CMD_PSE (1<<4) /* periodic schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
/* 3:2 is periodic frame list size */
#define CMD_RESET (1<<1) /* reset HC not bus */
#define CMD_RUN (1<<0) /* start/stop HC */
@@ -231,9 +238,9 @@ struct ehci_regs {
/* FRINDEX: offset 0x0C */
u32 frame_index; /* current microframe number */
/* CTRLDSSEGMENT: offset 0x10 */
- u32 segment; /* address bits 63:32 if needed */
+ u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */
- u32 frame_list; /* points to periodic list */
+ u32 frame_list; /* points to periodic list */
/* ASYNCLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */
@@ -302,7 +309,7 @@ struct ehci_dbg_port {
/*
* EHCI Specification 0.95 Section 3.5
- * QTD: describe data transfer components (buffer, direction, ...)
+ * QTD: describe data transfer components (buffer, direction, ...)
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
*
* These are associated only with "QH" (Queue Head) structures,
@@ -312,7 +319,7 @@ struct ehci_qtd {
/* first part defined by EHCI spec */
__le32 hw_next; /* see EHCI 3.5.1 */
__le32 hw_alt_next; /* see EHCI 3.5.2 */
- __le32 hw_token; /* see EHCI 3.5.3 */
+ __le32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
@@ -349,8 +356,8 @@ struct ehci_qtd {
/* values for that type tag */
#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1)
#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
-#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
-#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
+#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
+#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
@@ -367,7 +374,7 @@ struct ehci_qtd {
* For entries in the async schedule, the type tag always says "qh".
*/
union ehci_shadow {
- struct ehci_qh *qh; /* Q_TYPE_QH */
+ struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
@@ -397,7 +404,7 @@ struct ehci_qh {
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */
-
+
/* qtd overlay (hardware parts of a struct ehci_qtd) */
__le32 hw_qtd_next;
__le32 hw_alt_next;
@@ -472,7 +479,7 @@ struct ehci_iso_stream {
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
struct usb_device *udev;
- struct usb_host_endpoint *ep;
+ struct usb_host_endpoint *ep;
/* output of (re)scheduling */
unsigned long start; /* jiffies */
@@ -492,8 +499,8 @@ struct ehci_iso_stream {
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
- __le32 buf0;
- __le32 buf1;
+ __le32 buf0;
+ __le32 buf1;
__le32 buf2;
/* this is used to initialize sITD's tt info */
@@ -521,7 +528,7 @@ struct ehci_itd {
#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
- __le32 hw_bufp [7]; /* see EHCI 3.3.3 */
+ __le32 hw_bufp [7]; /* see EHCI 3.3.3 */
__le32 hw_bufp_hi [7]; /* Appendix B */
/* the rest is HCD-private */
@@ -542,7 +549,7 @@ struct ehci_itd {
/*-------------------------------------------------------------------------*/
/*
- * EHCI Specification 0.95 Section 3.4
+ * EHCI Specification 0.95 Section 3.4
* siTD, aka split-transaction isochronous Transfer Descriptor
* ... describe full speed iso xfers through TT in hubs
* see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 5147ed4a666..a72e041df8e 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1204,10 +1204,10 @@ static int isp116x_show_dbg(struct seq_file *s, void *unused)
static int isp116x_open_seq(struct inode *inode, struct file *file)
{
- return single_open(file, isp116x_show_dbg, inode->u.generic_ip);
+ return single_open(file, isp116x_show_dbg, inode->i_private);
}
-static struct file_operations isp116x_debug_fops = {
+static const struct file_operations isp116x_debug_fops = {
.open = isp116x_open_seq,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h
index a1b7c3813d3..b91e2edd9c5 100644
--- a/drivers/usb/host/isp116x.h
+++ b/drivers/usb/host/isp116x.h
@@ -233,7 +233,7 @@ static const int cc_to_error[16] = {
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
- /* DevNotResp */ -ETIMEDOUT,
+ /* DevNotResp */ -ETIME,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 85cc059705a..b466581beb4 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -193,7 +193,7 @@ ohci_at91_start (struct usb_hcd *hcd)
if ((ret = ohci_init(ohci)) < 0)
return ret;
- root->maxchild = board->ports;
+ ohci->num_ports = board->ports;
if ((ret = ohci_run(ohci)) < 0) {
err("can't start %s", hcd->self.bus_name);
@@ -221,6 +221,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/
.start = ohci_at91_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -239,7 +240,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
-
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -296,6 +297,7 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
if (!clocked) {
clk_enable(iclk);
clk_enable(fclk);
+ clocked = 1;
}
return 0;
@@ -310,6 +312,7 @@ MODULE_ALIAS("at91_ohci");
static struct platform_driver ohci_hcd_at91_driver = {
.probe = ohci_hcd_at91_drv_probe,
.remove = ohci_hcd_at91_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
.suspend = ohci_hcd_at91_drv_suspend,
.resume = ohci_hcd_at91_drv_resume,
.driver = {
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index f7a975d5db0..24e23c5783d 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -268,11 +268,8 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_au1xxx_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_au1xxx_suspend, -- tbd */
- /* resume: ohci_au1xxx_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -291,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -338,6 +336,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev)
static struct platform_driver ohci_hcd_au1xxx_driver = {
.probe = ohci_hcd_au1xxx_drv_probe,
.remove = ohci_hcd_au1xxx_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_au1xxx_drv_suspend, */
/*.resume = ohci_hcd_au1xxx_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 7bfffcbbd22..8293c1d4be3 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -477,7 +477,7 @@ show_async (struct class_device *class_dev, char *buf)
unsigned long flags;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ohci = hcd_to_ohci(hcd);
/* display control and bulk lists together, for simplicity */
@@ -510,7 +510,7 @@ show_periodic (struct class_device *class_dev, char *buf)
seen_count = 0;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ohci = hcd_to_ohci(hcd);
next = buf;
size = PAGE_SIZE;
@@ -607,7 +607,7 @@ show_registers (struct class_device *class_dev, char *buf)
u32 rdata;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ohci = hcd_to_ohci(hcd);
regs = ohci->regs;
next = buf;
@@ -667,6 +667,11 @@ show_registers (struct class_device *class_dev, char *buf)
size -= temp;
next += temp;
+ temp = scnprintf (next, size, "hub poll timer %s\n",
+ ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+ size -= temp;
+ next += temp;
+
/* roothub */
ohci_dump_roothub (ohci, 1, &next, &size);
@@ -680,10 +685,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ohci_hcd *ohci)
{
struct class_device *cldev = ohci_to_hcd(ohci)->self.class_dev;
+ int retval;
- class_device_create_file(cldev, &class_device_attr_async);
- class_device_create_file(cldev, &class_device_attr_periodic);
- class_device_create_file(cldev, &class_device_attr_registers);
+ retval = class_device_create_file(cldev, &class_device_attr_async);
+ retval = class_device_create_file(cldev, &class_device_attr_periodic);
+ retval = class_device_create_file(cldev, &class_device_attr_registers);
ohci_dbg (ohci, "created debug files\n");
}
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index 6531c4d2652..1bf5e7a4e73 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -128,12 +128,14 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
.flags = HCD_USB11 | HCD_MEMORY,
.start = ohci_ep93xx_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
.get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -202,6 +204,7 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
static struct platform_driver ohci_hcd_ep93xx_driver = {
.probe = ohci_hcd_ep93xx_drv_probe,
.remove = ohci_hcd_ep93xx_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_hcd_ep93xx_drv_suspend,
.resume = ohci_hcd_ep93xx_drv_resume,
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 94d8cf4b36c..d1d68c40225 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -88,7 +88,7 @@
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/reboot.h>
@@ -101,7 +101,7 @@
#include "../core/hcd.h"
-#define DRIVER_VERSION "2005 April 22"
+#define DRIVER_VERSION "2006 August 04"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
@@ -110,9 +110,10 @@
#undef OHCI_VERBOSE_DEBUG /* not always helpful */
/* For initializing controller (mask in an HCFS mode too) */
-#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT \
- (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+ (OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+ | OHCI_INTR_RD | OHCI_INTR_WDH)
#ifdef __hppa__
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
@@ -128,12 +129,13 @@
static const char hcd_name [] = "ohci_hcd";
+#define STATECHANGE_DELAY msecs_to_jiffies(300)
+
#include "ohci.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
static int ohci_init (struct ohci_hcd *ohci);
static void ohci_stop (struct usb_hcd *hcd);
-static int ohci_reboot (struct notifier_block *, unsigned long , void *);
#include "ohci-hub.c"
#include "ohci-dbg.c"
@@ -416,21 +418,20 @@ static void ohci_usb_reset (struct ohci_hcd *ohci)
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
}
-/* reboot notifier forcibly disables IRQs and DMA, helping kexec and
+/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and
* other cases where the next software may expect clean state from the
* "firmware". this is bus-neutral, unlike shutdown() methods.
*/
-static int
-ohci_reboot (struct notifier_block *block, unsigned long code, void *null)
+static void
+ohci_shutdown (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci;
- ohci = container_of (block, struct ohci_hcd, reboot_notifier);
+ ohci = hcd_to_ohci (hcd);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
ohci_usb_reset (ohci);
/* flush the writes */
(void) ohci_readl (ohci, &ohci->regs->control);
- return 0;
}
/*-------------------------------------------------------------------------*
@@ -446,7 +447,6 @@ static int ohci_init (struct ohci_hcd *ohci)
disable (ohci);
ohci->regs = hcd->regs;
- ohci->next_statechange = jiffies;
/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
* was never needed for most non-PCI systems ... remove the code?
@@ -502,7 +502,6 @@ static int ohci_init (struct ohci_hcd *ohci)
if ((ret = ohci_mem_init (ohci)) < 0)
ohci_stop (hcd);
else {
- register_reboot_notifier (&ohci->reboot_notifier);
create_debug_files (ohci);
}
@@ -637,10 +636,14 @@ retry:
return -EOVERFLOW;
}
- /* start controller operations */
+ /* use rhsc irqs after khubd is fully initialized */
+ hcd->poll_rh = 1;
+ hcd->uses_new_polling = 1;
+
+ /* start controller operations */
ohci->hc_control &= OHCI_CTRL_RWC;
- ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
- ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
hcd->state = HC_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
@@ -648,7 +651,7 @@ retry:
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_INIT;
- ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+ ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
ohci_writel (ohci, mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
@@ -672,6 +675,7 @@ retry:
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
spin_unlock_irq (&ohci->lock);
// POTPGT delay is bits 24-31, in 2 ms units.
@@ -709,7 +713,14 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
/* interrupt for some other device? */
} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
return IRQ_NOTMINE;
- }
+ }
+
+ if (ints & OHCI_INTR_RHSC) {
+ ohci_vdbg (ohci, "rhsc\n");
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+ ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
+ usb_hcd_poll_rh_status(hcd);
+ }
if (ints & OHCI_INTR_UE) {
disable (ohci);
@@ -723,13 +734,18 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
if (ints & OHCI_INTR_RD) {
ohci_vdbg (ohci, "resume detect\n");
ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus);
- if (hcd->state != HC_STATE_QUIESCING)
+ hcd->poll_rh = 1;
+ if (ohci->autostop) {
+ spin_lock (&ohci->lock);
+ ohci_rh_resume (ohci);
+ spin_unlock (&ohci->lock);
+ } else
usb_hcd_resume_root_hub(hcd);
}
if (ints & OHCI_INTR_WDH) {
if (HC_IS_RUNNING(hcd->state))
- ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
+ ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
spin_lock (&ohci->lock);
dl_done_list (ohci, ptregs);
spin_unlock (&ohci->lock);
@@ -775,9 +791,10 @@ static void ohci_stop (struct usb_hcd *hcd)
ohci_usb_reset (ohci);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
-
+ free_irq(hcd->irq, hcd);
+ hcd->irq = -1;
+
remove_debug_files (ohci);
- unregister_reboot_notifier (&ohci->reboot_notifier);
ohci_mem_cleanup (ohci);
if (ohci->hcca) {
dma_free_coherent (hcd->self.controller,
@@ -917,6 +934,10 @@ MODULE_LICENSE ("GPL");
#include "ohci-at91.c"
#endif
+#ifdef CONFIG_ARCH_PNX4008
+#include "ohci-pnx4008.c"
+#endif
+
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_S3C2410) \
@@ -928,6 +949,7 @@ MODULE_LICENSE ("GPL");
|| defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
|| defined (CONFIG_ARCH_AT91RM9200) \
|| defined (CONFIG_ARCH_AT91SAM9261) \
+ || defined (CONFIG_ARCH_PNX4008) \
)
#error "missing bus glue for ohci-hcd"
#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 5b0a23fd798..ec75774abea 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -36,27 +36,25 @@
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_PM
+/* hcd->hub_irq_enable() */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+}
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
static void dl_done_list (struct ohci_hcd *, struct pt_regs *);
static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *);
-static int ohci_restart (struct ohci_hcd *ohci);
-static int ohci_bus_suspend (struct usb_hcd *hcd)
+static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
+__releases(ohci->lock)
+__acquires(ohci->lock)
{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int status = 0;
- unsigned long flags;
-
- spin_lock_irqsave (&ohci->lock, flags);
-
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
- spin_unlock_irqrestore (&ohci->lock, flags);
- return -ESHUTDOWN;
- }
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
@@ -72,15 +70,16 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
ohci_dbg (ohci, "needs reinit!\n");
goto done;
case OHCI_USB_SUSPEND:
- ohci_dbg (ohci, "already suspended\n");
- goto done;
+ if (!ohci->autostop) {
+ ohci_dbg (ohci, "already suspended\n");
+ goto done;
+ }
}
- ohci_dbg (ohci, "suspend root hub\n");
+ ohci_dbg (ohci, "%s root hub\n",
+ autostop ? "auto-stop" : "suspend");
/* First stop any processing */
- if (ohci->hc_control & OHCI_SCHED_ENABLES) {
- int limit;
-
+ if (!autostop && (ohci->hc_control & OHCI_SCHED_ENABLES)) {
ohci->hc_control &= ~OHCI_SCHED_ENABLES;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
@@ -90,27 +89,22 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
* then the last WDH could take 6+ msec
*/
ohci_dbg (ohci, "stopping schedules ...\n");
- limit = 2000;
- while (limit > 0) {
- udelay (250);
- limit =- 250;
- if (ohci_readl (ohci, &ohci->regs->intrstatus)
- & OHCI_INTR_SF)
- break;
- }
- dl_done_list (ohci, NULL);
- mdelay (7);
+ ohci->autostop = 0;
+ spin_unlock_irq (&ohci->lock);
+ msleep (8);
+ spin_lock_irq (&ohci->lock);
}
dl_done_list (ohci, NULL);
finish_unlinks (ohci, ohci_frame_no(ohci), NULL);
- ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
- &ohci->regs->intrstatus);
/* maybe resume can wake root hub */
- if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev))
+ if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev) ||
+ autostop)
ohci->hc_control |= OHCI_CTRL_RWE;
- else
+ else {
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
ohci->hc_control &= ~OHCI_CTRL_RWE;
+ }
/* Suspend hub ... this is the "global (to this bus) suspend" mode,
* which doesn't imply ports will first be individually suspended.
@@ -121,13 +115,12 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
(void) ohci_readl (ohci, &ohci->regs->control);
/* no resumes until devices finish suspending */
- ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+ if (!autostop) {
+ ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+ ohci->autostop = 0;
+ }
done:
- /* external suspend vs self autosuspend ... same effect */
- if (status == 0)
- usb_hcd_suspend_root_hub(hcd);
- spin_unlock_irqrestore (&ohci->lock, flags);
return status;
}
@@ -139,25 +132,19 @@ static inline struct ed *find_head (struct ed *ed)
return ed;
}
+static int ohci_restart (struct ohci_hcd *ohci);
+
/* caller has locked the root hub */
-static int ohci_bus_resume (struct usb_hcd *hcd)
+static int ohci_rh_resume (struct ohci_hcd *ohci)
+__releases(ohci->lock)
+__acquires(ohci->lock)
{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct usb_hcd *hcd = ohci_to_hcd (ohci);
u32 temp, enables;
int status = -EINPROGRESS;
- unsigned long flags;
-
- if (time_before (jiffies, ohci->next_statechange))
- msleep(5);
-
- spin_lock_irqsave (&ohci->lock, flags);
-
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
- spin_unlock_irqrestore (&ohci->lock, flags);
- return -ESHUTDOWN;
- }
-
+ int autostopped = ohci->autostop;
+ ohci->autostop = 0;
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
@@ -177,7 +164,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
ohci->hc_control |= OHCI_USB_RESUME;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
- ohci_dbg (ohci, "resume root hub\n");
+ ohci_dbg (ohci, "%s root hub\n",
+ autostopped ? "auto-start" : "resume");
break;
case OHCI_USB_RESUME:
/* HCFS changes sometime after INTR_RD */
@@ -192,16 +180,24 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
ohci_dbg (ohci, "lost power\n");
status = -EBUSY;
}
- spin_unlock_irqrestore (&ohci->lock, flags);
+#ifdef CONFIG_PM
if (status == -EBUSY) {
- (void) ohci_init (ohci);
- return ohci_restart (ohci);
+ if (!autostopped) {
+ spin_unlock_irq (&ohci->lock);
+ (void) ohci_init (ohci);
+ status = ohci_restart (ohci);
+ spin_lock_irq (&ohci->lock);
+ }
+ return status;
}
+#endif
if (status != -EINPROGRESS)
return status;
+ if (autostopped)
+ goto skip_resume;
+ spin_unlock_irq (&ohci->lock);
temp = ohci->num_ports;
- enables = 0;
while (temp--) {
u32 stat = ohci_readl (ohci,
&ohci->regs->roothub.portstatus [temp]);
@@ -234,17 +230,21 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
/* Sometimes PCI D3 suspend trashes frame timings ... */
periodic_reinit (ohci);
+ /* the following code is executed with ohci->lock held and
+ * irqs disabled if and only if autostopped is true
+ */
+
+skip_resume:
/* interrupts might have been disabled */
ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
if (ohci->ed_rm_list)
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
- ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
- &ohci->regs->intrstatus);
/* Then re-enable operations */
ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
- msleep (3);
+ if (!autostopped)
+ msleep (3);
temp = ohci->hc_control;
temp &= OHCI_CTRL_RWC;
@@ -254,10 +254,14 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
(void) ohci_readl (ohci, &ohci->regs->control);
/* TRSMRCY */
- msleep (10);
+ if (!autostopped) {
+ msleep (10);
+ spin_lock_irq (&ohci->lock);
+ }
+ /* now ohci->lock is always held and irqs are always disabled */
- /* keep it alive for ~5x suspend + resume costs */
- ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+ /* keep it alive for more than ~5x suspend + resume costs */
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
/* maybe turn schedules back on */
enables = 0;
@@ -291,6 +295,45 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
return 0;
}
+#ifdef CONFIG_PM
+
+static int ohci_bus_suspend (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int rc;
+
+ spin_lock_irq (&ohci->lock);
+
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ rc = -ESHUTDOWN;
+ else
+ rc = ohci_rh_suspend (ohci, 0);
+ spin_unlock_irq (&ohci->lock);
+ return rc;
+}
+
+static int ohci_bus_resume (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int rc;
+
+ if (time_before (jiffies, ohci->next_statechange))
+ msleep(5);
+
+ spin_lock_irq (&ohci->lock);
+
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ rc = -ESHUTDOWN;
+ else
+ rc = ohci_rh_resume (ohci);
+ spin_unlock_irq (&ohci->lock);
+
+ /* poll until we know a device is connected or we autostop */
+ if (rc == 0)
+ usb_hcd_poll_rh_status(hcd);
+ return rc;
+}
+
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
@@ -302,20 +345,11 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
- int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
+ int any_connected = 0, rhsc_enabled = 1;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
- /* handle autosuspended root: finish resuming before
- * letting khubd or root hub timer see state changes.
- */
- if (unlikely((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
- || !HC_IS_RUNNING(hcd->state))) {
- can_suspend = 0;
- goto done;
- }
-
/* undocumented erratum seen on at least rev D */
if ((ohci->flags & OHCI_QUIRK_AMD756)
&& (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) {
@@ -339,6 +373,9 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
for (i = 0; i < ohci->num_ports; i++) {
u32 status = roothub_portstatus (ohci, i);
+ /* can't autostop if ports are connected */
+ any_connected |= (status & RH_PS_CCS);
+
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
@@ -346,39 +383,72 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
- continue;
}
-
- /* can suspend if no ports are enabled; or if all all
- * enabled ports are suspended AND remote wakeup is on.
- */
- if (!(status & RH_PS_CCS))
- continue;
- if ((status & RH_PS_PSS) && can_suspend)
- continue;
- can_suspend = 0;
}
-done:
- spin_unlock_irqrestore (&ohci->lock, flags);
-#ifdef CONFIG_PM
- /* save power by suspending idle root hubs;
- * INTR_RD wakes us when there's work
+ /* NOTE: vendors didn't always make the same implementation
+ * choices for RHSC. Sometimes it triggers on an edge (like
+ * setting and maybe clearing a port status change bit); and
+ * it's level-triggered on other silicon, active until khubd
+ * clears all active port status change bits. If it's still
+ * set (level-triggered) we must disable it and rely on
+ * polling until khubd re-enables it.
*/
- if (can_suspend
- && !changed
- && !ohci->ed_rm_list
- && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
- & ohci->hc_control)
- == OHCI_USB_OPER
- && time_after (jiffies, ohci->next_statechange)
- && usb_trylock_device (hcd->self.root_hub) == 0
- ) {
- ohci_vdbg (ohci, "autosuspend\n");
- (void) ohci_bus_suspend (hcd);
- usb_unlock_device (hcd->self.root_hub);
+ if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) {
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
+ (void) ohci_readl (ohci, &ohci->regs->intrdisable);
+ rhsc_enabled = 0;
}
-#endif
+ hcd->poll_rh = 1;
+
+ /* carry out appropriate state changes */
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+
+ case OHCI_USB_OPER:
+ /* keep on polling until we know a device is connected
+ * and RHSC is enabled */
+ if (!ohci->autostop) {
+ if (any_connected) {
+ if (rhsc_enabled)
+ hcd->poll_rh = 0;
+ } else {
+ ohci->autostop = 1;
+ ohci->next_statechange = jiffies + HZ;
+ }
+
+ /* if no devices have been attached for one second, autostop */
+ } else {
+ if (changed || any_connected) {
+ ohci->autostop = 0;
+ ohci->next_statechange = jiffies +
+ STATECHANGE_DELAY;
+ } else if (time_after_eq (jiffies,
+ ohci->next_statechange)
+ && !ohci->ed_rm_list
+ && !(ohci->hc_control &
+ OHCI_SCHED_ENABLES)) {
+ ohci_rh_suspend (ohci, 1);
+ }
+ }
+ break;
+
+ /* if there is a port change, autostart or ask to be resumed */
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ if (changed) {
+ if (ohci->autostop)
+ ohci_rh_resume (ohci);
+ else
+ usb_hcd_resume_root_hub (hcd);
+ } else {
+ /* everything is idle, no need for polling */
+ hcd->poll_rh = 0;
+ }
+ break;
+ }
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
return changed ? length : 0;
}
@@ -550,9 +620,6 @@ static int ohci_hub_control (
break;
case USB_PORT_FEAT_SUSPEND:
temp = RH_PS_POCI;
- if ((ohci->hc_control & OHCI_CTRL_HCFS)
- != OHCI_USB_OPER)
- usb_hcd_resume_root_hub(hcd);
break;
case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC;
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
index 5602da9bd52..e121d97ed91 100644
--- a/drivers/usb/host/ohci-lh7a404.c
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -173,11 +173,8 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_lh7a404_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_lh7a404_suspend, -- tbd */
- /* resume: ohci_lh7a404_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -196,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -244,6 +242,7 @@ static int ohci_hcd_lh7a404_drv_resume(struct platform_device *dev)
static struct platform_driver ohci_hcd_lh7a404_driver = {
.probe = ohci_hcd_lh7a404_drv_probe,
.remove = ohci_hcd_lh7a404_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_lh7a404_drv_suspend, */
/*.resume = ohci_hcd_lh7a404_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
index bfbe328a478..d976614eebd 100644
--- a/drivers/usb/host/ohci-mem.c
+++ b/drivers/usb/host/ohci-mem.c
@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
- ohci->reboot_notifier.notifier_call = ohci_reboot;
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index c4c4babd476..9c02177de50 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -4,7 +4,7 @@
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2005 David Brownell
* (C) Copyright 2002 Hewlett-Packard Company
- *
+ *
* OMAP Bus Glue
*
* Modified for OMAP by Tony Lindgren <tony@atomide.com>
@@ -66,15 +66,20 @@ extern int usb_disabled(void);
extern int ocpi_enable(void);
static struct clk *usb_host_ck;
+static struct clk *usb_dc_ck;
+static int host_enabled;
+static int host_initialized;
static void omap_ohci_clock_power(int on)
{
if (on) {
+ clk_enable(usb_dc_ck);
clk_enable(usb_host_ck);
/* guesstimate for T5 == 1x 32K clock + APLL lock time */
udelay(100);
} else {
clk_disable(usb_host_ck);
+ clk_disable(usb_dc_ck);
}
}
@@ -87,14 +92,14 @@ static int omap_ohci_transceiver_power(int on)
if (on) {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
- | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, LOW);
} else {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
- & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, HIGH);
@@ -103,6 +108,7 @@ static int omap_ohci_transceiver_power(int on)
return 0;
}
+#ifdef CONFIG_ARCH_OMAP15XX
/*
* OMAP-1510 specific Local Bus clock on/off
*/
@@ -121,8 +127,8 @@ static int omap_1510_local_bus_power(int on)
/*
* OMAP-1510 specific Local Bus initialization
* NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
- * See also arch/mach-omap/memory.h for __virt_to_dma() and
- * __dma_to_virt() which need to match with the physical
+ * See also arch/mach-omap/memory.h for __virt_to_dma() and
+ * __dma_to_virt() which need to match with the physical
* Local Bus address below.
*/
static int omap_1510_local_bus_init(void)
@@ -130,7 +136,7 @@ static int omap_1510_local_bus_init(void)
unsigned int tlb;
unsigned long lbaddr, physaddr;
- omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
+ omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
OMAP1510_LB_CLOCK_DIV);
/* Configure the Local Bus MMU table */
@@ -138,7 +144,7 @@ static int omap_1510_local_bus_init(void)
lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
physaddr = tlb * 0x00100000 + PHYS_OFFSET;
omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
- omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
+ omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
OMAP1510_LB_MMU_CAM_L);
omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
@@ -152,6 +158,10 @@ static int omap_1510_local_bus_init(void)
return 0;
}
+#else
+#define omap_1510_local_bus_power(x) {}
+#define omap_1510_local_bus_init() {}
+#endif
#ifdef CONFIG_USB_OTG
@@ -173,13 +183,14 @@ static void start_hnp(struct ohci_hcd *ohci)
/*-------------------------------------------------------------------------*/
-static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
+static int ohci_omap_init(struct usb_hcd *hcd)
{
- struct omap_usb_config *config = pdev->dev.platform_data;
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct omap_usb_config *config = hcd->self.controller->platform_data;
int need_transceiver = (config->otg != 0);
int ret;
- dev_dbg(&pdev->dev, "starting USB Controller\n");
+ dev_dbg(hcd->self.controller, "starting USB Controller\n");
if (config->otg) {
ohci_to_hcd(ohci)->self.otg_port = config->otg;
@@ -200,7 +211,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
if (ohci->transceiver) {
int status = otg_set_host(ohci->transceiver,
&ohci_to_hcd(ohci)->self);
- dev_dbg(&pdev->dev, "init %s transceiver, status %d\n",
+ dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n",
ohci->transceiver->label, status);
if (status) {
if (ohci->transceiver)
@@ -208,7 +219,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
return status;
}
} else {
- dev_err(&pdev->dev, "can't find transceiver\n");
+ dev_err(hcd->self.controller, "can't find transceiver\n");
return -ENODEV;
}
}
@@ -247,6 +258,10 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
}
ohci_writel(ohci, rh, &ohci->regs->roothub.a);
distrust_firmware = 0;
+ } else if (machine_is_nokia770()) {
+ /* We require a self-powered hub, which should have
+ * plenty of power. */
+ ohci_to_hcd(ohci)->power_budget = 0;
}
/* FIXME khubd hub requests should manage power switching */
@@ -260,21 +275,15 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
return 0;
}
-static void omap_stop_hc(struct platform_device *pdev)
+static void ohci_omap_stop(struct usb_hcd *hcd)
{
- dev_dbg(&pdev->dev, "stopping USB Controller\n");
+ dev_dbg(hcd->self.controller, "stopping USB Controller\n");
omap_ohci_clock_power(0);
}
/*-------------------------------------------------------------------------*/
-void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
-
-/* configure so an HC device and id are always provided */
-/* always called with process context; sleeping is OK */
-
-
/**
* usb_hcd_omap_probe - initialize OMAP-based HCDs
* Context: !in_interrupt()
@@ -283,7 +292,7 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*/
-int usb_hcd_omap_probe (const struct hc_driver *driver,
+static int usb_hcd_omap_probe (const struct hc_driver *driver,
struct platform_device *pdev)
{
int retval, irq;
@@ -291,12 +300,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
struct ohci_hcd *ohci;
if (pdev->num_resources != 2) {
- printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
+ printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
pdev->num_resources);
return -ENODEV;
}
- if (pdev->resource[0].flags != IORESOURCE_MEM
+ if (pdev->resource[0].flags != IORESOURCE_MEM
|| pdev->resource[1].flags != IORESOURCE_IRQ) {
printk(KERN_ERR "hcd probe: invalid resource type\n");
return -ENODEV;
@@ -306,6 +315,17 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
if (IS_ERR(usb_host_ck))
return PTR_ERR(usb_host_ck);
+ if (!cpu_is_omap1510())
+ usb_dc_ck = clk_get(0, "usb_dc_ck");
+ else
+ usb_dc_ck = clk_get(0, "lb_ck");
+
+ if (IS_ERR(usb_dc_ck)) {
+ clk_put(usb_host_ck);
+ return PTR_ERR(usb_dc_ck);
+ }
+
+
hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
if (!hcd) {
retval = -ENOMEM;
@@ -325,9 +345,8 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
- retval = omap_start_hc(ohci, pdev);
- if (retval < 0)
- goto err2;
+ host_initialized = 0;
+ host_enabled = 1;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -335,15 +354,21 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
goto err2;
}
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
- if (retval == 0)
- return retval;
+ if (retval)
+ goto err2;
+
+ host_initialized = 1;
+
+ if (!host_enabled)
+ omap_ohci_clock_power(0);
- omap_stop_hc(pdev);
+ return 0;
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
err0:
+ clk_put(usb_dc_ck);
clk_put(usb_host_ck);
return retval;
}
@@ -359,31 +384,41 @@ err0:
* Reverses the effect of usb_hcd_omap_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
- *
*/
-void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+static inline void
+usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
usb_remove_hcd(hcd);
+ if (ohci->transceiver) {
+ (void) otg_set_host(ohci->transceiver, 0);
+ put_device(ohci->transceiver->dev);
+ }
if (machine_is_omap_osk())
omap_free_gpio(9);
- omap_stop_hc(pdev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
+ clk_put(usb_dc_ck);
clk_put(usb_host_ck);
}
/*-------------------------------------------------------------------------*/
-static int __devinit
+static int
ohci_omap_start (struct usb_hcd *hcd)
{
struct omap_usb_config *config;
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
+ if (!host_enabled)
+ return 0;
config = hcd->self.controller->platform_data;
- if (config->otg || config->rwc)
+ if (config->otg || config->rwc) {
+ ohci->hc_control = OHCI_CTRL_RWC;
writel(OHCI_CTRL_RWC, &ohci->regs->control);
+ }
if ((ret = ohci_run (ohci)) < 0) {
dev_err(hcd->self.controller, "can't start\n");
@@ -409,8 +444,10 @@ static const struct hc_driver ohci_omap_hc_driver = {
/*
* basic lifecycle operations
*/
+ .reset = ohci_omap_init,
.start = ohci_omap_start,
- .stop = ohci_stop,
+ .stop = ohci_omap_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -429,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -446,13 +484,8 @@ static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
usb_hcd_omap_remove(hcd, dev);
- if (ohci->transceiver) {
- (void) otg_set_host(ohci->transceiver, 0);
- put_device(ohci->transceiver->dev);
- }
platform_set_drvdata(dev, NULL);
return 0;
@@ -472,7 +505,7 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
omap_ohci_clock_power(0);
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
- dev->power.power_state = PMSG_SUSPEND;
+ dev->dev.power.power_state = PMSG_SUSPEND;
return 0;
}
@@ -485,8 +518,8 @@ static int ohci_omap_resume(struct platform_device *dev)
ohci->next_statechange = jiffies;
omap_ohci_clock_power(1);
- dev->power.power_state = PMSG_ON;
- usb_hcd_resume_root_hub(dev_get_drvdata(dev));
+ dev->dev.power.power_state = PMSG_ON;
+ usb_hcd_resume_root_hub(platform_get_drvdata(dev));
return 0;
}
@@ -500,6 +533,7 @@ static int ohci_omap_resume(struct platform_device *dev)
static struct platform_driver ohci_hcd_omap_driver = {
.probe = ohci_hcd_omap_drv_probe,
.remove = ohci_hcd_omap_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_omap_suspend,
.resume = ohci_omap_resume,
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index b268537e389..87441855278 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -73,13 +73,14 @@ ohci_pci_start (struct usb_hcd *hcd)
else if (pdev->vendor == PCI_VENDOR_ID_NS) {
struct pci_dev *b;
- b = pci_find_slot (pdev->bus->number,
+ b = pci_get_slot (pdev->bus,
PCI_DEVFN (PCI_SLOT (pdev->devfn), 1));
if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
&& b->vendor == PCI_VENDOR_ID_NS) {
ohci->flags |= OHCI_QUIRK_SUPERIO;
ohci_dbg (ohci, "Using NSC SuperIO setup\n");
}
+ pci_dev_put(b);
}
/* Check for Compaq's ZFMicro chipset, which needs short
@@ -135,6 +136,11 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
}
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
(void)ohci_readl(ohci, &ohci->regs->intrdisable);
+
+ /* make sure snapshot being resumed re-enumerates everything */
+ if (message.event == PM_EVENT_PRETHAW)
+ ohci_usb_reset(ohci);
+
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail:
spin_unlock_irqrestore (&ohci->lock, flags);
@@ -171,11 +177,14 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.reset = ohci_pci_reset,
.start = ohci_pci_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
#ifdef CONFIG_PM
+ /* these suspend/resume entries are for upstream PCI glue ONLY */
.suspend = ohci_pci_suspend,
.resume = ohci_pci_resume,
#endif
- .stop = ohci_stop,
/*
* managing i/o requests and associated device resources
@@ -194,6 +203,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -224,6 +234,8 @@ static struct pci_driver ohci_pci_driver = {
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif
+
+ .shutdown = usb_hcd_pci_shutdown,
};
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
new file mode 100644
index 00000000000..82cb22f002e
--- /dev/null
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -0,0 +1,476 @@
+/*
+ * drivers/usb/host/ohci-pnx4008.c
+ *
+ * driver for Philips PNX4008 USB Host
+ *
+ * Authors: Dmitry Chigirev <source@mvista.com>
+ * Vitaly Wool <vitalywool@gmail.com>
+ *
+ * register initialization is based on code examples provided by Philips
+ * Copyright (c) 2005 Koninklijke Philips Electronics N.V.
+ *
+ * NOTE: This driver does not have suspend/resume functionality
+ * This driver is intended for engineering development purposes only
+ *
+ * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/platform.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+
+#define USB_CTRL IO_ADDRESS(PNX4008_PWRMAN_BASE + 0x64)
+
+/* USB_CTRL bit defines */
+#define USB_SLAVE_HCLK_EN (1 << 24)
+#define USB_HOST_NEED_CLK_EN (1 << 21)
+
+#define USB_OTG_CLK_CTRL IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF4)
+#define USB_OTG_CLK_STAT IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF8)
+
+/* USB_OTG_CLK_CTRL bit defines */
+#define AHB_M_CLOCK_ON (1 << 4)
+#define OTG_CLOCK_ON (1 << 3)
+#define I2C_CLOCK_ON (1 << 2)
+#define DEV_CLOCK_ON (1 << 1)
+#define HOST_CLOCK_ON (1 << 0)
+
+#define USB_OTG_STAT_CONTROL IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0x110)
+
+/* USB_OTG_STAT_CONTROL bit defines */
+#define TRANSPARENT_I2C_EN (1 << 7)
+#define HOST_EN (1 << 0)
+
+/* ISP1301 USB transceiver I2C registers */
+#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */
+
+#define MC1_SPEED_REG (1 << 0)
+#define MC1_SUSPEND_REG (1 << 1)
+#define MC1_DAT_SE0 (1 << 2)
+#define MC1_TRANSPARENT (1 << 3)
+#define MC1_BDIS_ACON_EN (1 << 4)
+#define MC1_OE_INT_EN (1 << 5)
+#define MC1_UART_EN (1 << 6)
+#define MC1_MASK 0x7f
+
+#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */
+
+#define MC2_GLOBAL_PWR_DN (1 << 0)
+#define MC2_SPD_SUSP_CTRL (1 << 1)
+#define MC2_BI_DI (1 << 2)
+#define MC2_TRANSP_BDIR0 (1 << 3)
+#define MC2_TRANSP_BDIR1 (1 << 4)
+#define MC2_AUDIO_EN (1 << 5)
+#define MC2_PSW_EN (1 << 6)
+#define MC2_EN2V7 (1 << 7)
+
+#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */
+# define OTG1_DP_PULLUP (1 << 0)
+# define OTG1_DM_PULLUP (1 << 1)
+# define OTG1_DP_PULLDOWN (1 << 2)
+# define OTG1_DM_PULLDOWN (1 << 3)
+# define OTG1_ID_PULLDOWN (1 << 4)
+# define OTG1_VBUS_DRV (1 << 5)
+# define OTG1_VBUS_DISCHRG (1 << 6)
+# define OTG1_VBUS_CHRG (1 << 7)
+#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */
+# define OTG_B_SESS_END (1 << 6)
+# define OTG_B_SESS_VLD (1 << 7)
+
+#define ISP1301_I2C_ADDR 0x2C
+
+#define ISP1301_I2C_MODE_CONTROL_1 0x4
+#define ISP1301_I2C_MODE_CONTROL_2 0x12
+#define ISP1301_I2C_OTG_CONTROL_1 0x6
+#define ISP1301_I2C_OTG_CONTROL_2 0x10
+#define ISP1301_I2C_INTERRUPT_SOURCE 0x8
+#define ISP1301_I2C_INTERRUPT_LATCH 0xA
+#define ISP1301_I2C_INTERRUPT_FALLING 0xC
+#define ISP1301_I2C_INTERRUPT_RISING 0xE
+#define ISP1301_I2C_REG_CLEAR_ADDR 1
+
+struct i2c_driver isp1301_driver;
+struct i2c_client *isp1301_i2c_client;
+
+extern int usb_disabled(void);
+extern int ocpi_enable(void);
+
+static struct clk *usb_clk;
+
+static int isp1301_probe(struct i2c_adapter *adap);
+static int isp1301_detach(struct i2c_client *client);
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+ void *arg);
+
+static unsigned short normal_i2c[] =
+ { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END };
+static unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = dummy_i2c_addrlist,
+ .ignore = dummy_i2c_addrlist,
+};
+
+struct i2c_driver isp1301_driver = {
+ .id = I2C_DRIVERID_I2CDEV, /* Fake Id */
+ .class = I2C_CLASS_HWMON,
+ .attach_adapter = isp1301_probe,
+ .detach_client = isp1301_detach,
+ .command = isp1301_command
+};
+
+static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct i2c_client *c;
+
+ c = (struct i2c_client *)kzalloc(sizeof(*c), SLAB_KERNEL);
+
+ if (!c)
+ return -ENOMEM;
+
+ strcpy(c->name, "isp1301");
+ c->flags = 0;
+ c->addr = addr;
+ c->adapter = adap;
+ c->driver = &isp1301_driver;
+
+ isp1301_i2c_client = c;
+
+ return i2c_attach_client(c);
+}
+
+static int isp1301_probe(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, isp1301_attach);
+}
+
+static int isp1301_detach(struct i2c_client *client)
+{
+ i2c_detach_client(client);
+ kfree(isp1301_i2c_client);
+ return 0;
+}
+
+/* No commands defined */
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ return 0;
+}
+
+static void i2c_write(u8 buf, u8 subaddr)
+{
+ char tmpbuf[2];
+
+ tmpbuf[0] = subaddr; /*register number */
+ tmpbuf[1] = buf; /*register data */
+ i2c_master_send(isp1301_i2c_client, &tmpbuf[0], 2);
+}
+
+static void isp1301_configure(void)
+{
+ /* PNX4008 only supports DAT_SE0 USB mode */
+ /* PNX4008 R2A requires setting the MAX603 to output 3.6V */
+ /* Power up externel charge-pump */
+
+ i2c_write(MC1_DAT_SE0 | MC1_SPEED_REG, ISP1301_I2C_MODE_CONTROL_1);
+ i2c_write(~(MC1_DAT_SE0 | MC1_SPEED_REG),
+ ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL,
+ ISP1301_I2C_MODE_CONTROL_2);
+ i2c_write(~(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL),
+ ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN,
+ ISP1301_I2C_OTG_CONTROL_1);
+ i2c_write(~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN),
+ ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(0xFF,
+ ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(0xFF,
+ ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(0xFF,
+ ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR);
+
+}
+
+static inline void isp1301_vbus_on(void)
+{
+ i2c_write(OTG1_VBUS_DRV, ISP1301_I2C_OTG_CONTROL_1);
+}
+
+static inline void isp1301_vbus_off(void)
+{
+ i2c_write(OTG1_VBUS_DRV,
+ ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+}
+
+static void pnx4008_start_hc(void)
+{
+ unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
+ __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+ isp1301_vbus_on();
+}
+
+static void pnx4008_stop_hc(void)
+{
+ unsigned long tmp;
+ isp1301_vbus_off();
+ tmp = __raw_readl(USB_OTG_STAT_CONTROL) & ~HOST_EN;
+ __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+}
+
+static int __devinit ohci_pnx4008_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run(ohci)) < 0) {
+ dev_err(hcd->self.controller, "can't start\n");
+ ohci_stop(hcd);
+ return ret;
+ }
+ return 0;
+}
+
+static const struct hc_driver ohci_pnx4008_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "pnx4008 OHCI",
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_pnx4008_start,
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+
+ .start_port_reset = ohci_start_port_reset,
+};
+
+#define USB_CLOCK_MASK (AHB_M_CLOCK_ON| OTG_CLOCK_ON | HOST_CLOCK_ON | I2C_CLOCK_ON)
+
+static void pnx4008_set_usb_bits(void)
+{
+ start_int_set_falling_edge(SE_USB_OTG_ATX_INT_N);
+ start_int_ack(SE_USB_OTG_ATX_INT_N);
+ start_int_umask(SE_USB_OTG_ATX_INT_N);
+
+ start_int_set_rising_edge(SE_USB_OTG_TIMER_INT);
+ start_int_ack(SE_USB_OTG_TIMER_INT);
+ start_int_umask(SE_USB_OTG_TIMER_INT);
+
+ start_int_set_rising_edge(SE_USB_I2C_INT);
+ start_int_ack(SE_USB_I2C_INT);
+ start_int_umask(SE_USB_I2C_INT);
+
+ start_int_set_rising_edge(SE_USB_INT);
+ start_int_ack(SE_USB_INT);
+ start_int_umask(SE_USB_INT);
+
+ start_int_set_rising_edge(SE_USB_NEED_CLK_INT);
+ start_int_ack(SE_USB_NEED_CLK_INT);
+ start_int_umask(SE_USB_NEED_CLK_INT);
+
+ start_int_set_rising_edge(SE_USB_AHB_NEED_CLK_INT);
+ start_int_ack(SE_USB_AHB_NEED_CLK_INT);
+ start_int_umask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static void pnx4008_unset_usb_bits(void)
+{
+ start_int_mask(SE_USB_OTG_ATX_INT_N);
+ start_int_mask(SE_USB_OTG_TIMER_INT);
+ start_int_mask(SE_USB_I2C_INT);
+ start_int_mask(SE_USB_INT);
+ start_int_mask(SE_USB_NEED_CLK_INT);
+ start_int_mask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = 0;
+ struct ohci_hcd *ohci;
+ const struct hc_driver *driver = &ohci_pnx4008_hc_driver;
+
+ int ret = 0, irq;
+
+ dev_dbg(&pdev->dev, "%s: " DRIVER_INFO " (pnx4008)\n", hcd_name);
+ if (usb_disabled()) {
+ err("USB is disabled");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (pdev->num_resources != 2
+ || pdev->resource[0].flags != IORESOURCE_MEM
+ || pdev->resource[1].flags != IORESOURCE_IRQ) {
+ err("Invalid resource configuration");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* Enable AHB slave USB clock, needed for further USB clock control */
+ __raw_writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL);
+
+ ret = i2c_add_driver(&isp1301_driver);
+ if (ret < 0) {
+ err("failed to connect I2C to ISP1301 USB Transceiver");
+ goto out;
+ }
+
+ isp1301_configure();
+
+ /* Enable USB PLL */
+ usb_clk = clk_get(&pdev->dev, "ck_pll5");
+ if (IS_ERR(usb_clk)) {
+ err("failed to acquire USB PLL");
+ ret = PTR_ERR(usb_clk);
+ goto out1;
+ }
+
+ ret = clk_enable(usb_clk);
+ if (ret < 0) {
+ err("failed to start USB PLL");
+ goto out2;
+ }
+
+ ret = clk_set_rate(usb_clk, 48000);
+ if (ret < 0) {
+ err("failed to set USB clock rate");
+ goto out3;
+ }
+
+ __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
+
+ /* Set to enable all needed USB clocks */
+ __raw_writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL);
+
+ while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) !=
+ USB_CLOCK_MASK) ;
+
+ hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ err("Failed to allocate HC buffer");
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ /* Set all USB bits in the Start Enable register */
+ pnx4008_set_usb_bits();
+
+ hcd->rsrc_start = pdev->resource[0].start;
+ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ ret = -ENOMEM;
+ goto out4;
+ }
+ hcd->regs = (void __iomem *)pdev->resource[0].start;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENXIO;
+ goto out4;
+ }
+
+ hcd->self.hcpriv = (void *)hcd;
+
+ pnx4008_start_hc();
+ platform_set_drvdata(pdev, hcd);
+ ohci = hcd_to_ohci(hcd);
+ ohci_hcd_init(ohci);
+
+ dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
+ ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+ if (ret == 0)
+ return ret;
+
+ pnx4008_stop_hc();
+out4:
+ pnx4008_unset_usb_bits();
+ usb_put_hcd(hcd);
+out3:
+ clk_disable(usb_clk);
+out2:
+ clk_put(usb_clk);
+out1:
+ i2c_del_driver(&isp1301_driver);
+out:
+ return ret;
+}
+
+static int usb_hcd_pnx4008_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ pnx4008_stop_hc();
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ pnx4008_unset_usb_bits();
+ clk_disable(usb_clk);
+ clk_put(usb_clk);
+ i2c_del_driver(&isp1301_driver);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver usb_hcd_pnx4008_driver = {
+ .driver = {
+ .name = "usb-ohci",
+ },
+ .probe = usb_hcd_pnx4008_probe,
+ .remove = usb_hcd_pnx4008_remove,
+};
+
+static int __init usb_hcd_pnx4008_init(void)
+{
+ return platform_driver_register(&usb_hcd_pnx4008_driver);
+}
+
+static void __exit usb_hcd_pnx4008_cleanup(void)
+{
+ return platform_driver_unregister(&usb_hcd_pnx4008_driver);
+}
+
+module_init(usb_hcd_pnx4008_init);
+module_exit(usb_hcd_pnx4008_cleanup);
diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
index 9fe56ff1615..d9d1ae236bd 100644
--- a/drivers/usb/host/ohci-ppc-soc.c
+++ b/drivers/usb/host/ohci-ppc-soc.c
@@ -148,6 +148,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
*/
.start = ohci_ppc_soc_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -166,6 +167,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -195,6 +197,7 @@ static int ohci_hcd_ppc_soc_drv_remove(struct platform_device *pdev)
static struct platform_driver ohci_hcd_ppc_soc_driver = {
.probe = ohci_hcd_ppc_soc_drv_probe,
.remove = ohci_hcd_ppc_soc_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
/*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/
/*.resume = ohci_hcd_ppc_soc_drv_resume,*/
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 6f559e10278..e176b04d7ae 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -270,6 +270,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
*/
.start = ohci_pxa27x_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -288,6 +289,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -357,6 +359,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev)
static struct platform_driver ohci_hcd_pxa27x_driver = {
.probe = ohci_hcd_pxa27x_drv_probe,
.remove = ohci_hcd_pxa27x_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_hcd_pxa27x_drv_suspend,
.resume = ohci_hcd_pxa27x_drv_resume,
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index d2fc6969a9f..59e436424d4 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -370,7 +370,7 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
goto err_mem;
}
- usb_clk = clk_get(&dev->dev, "upll");
+ usb_clk = clk_get(&dev->dev, "usb-bus-host");
if (IS_ERR(usb_clk)) {
dev_err(&dev->dev, "cannot get usb-host clock\n");
retval = -ENOENT;
@@ -447,6 +447,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
*/
.start = ohci_s3c2410_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -465,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
*/
.hub_status_data = ohci_s3c2410_hub_status_data,
.hub_control = ohci_s3c2410_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -490,6 +492,7 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)
static struct platform_driver ohci_hcd_s3c2410_driver = {
.probe = ohci_hcd_s3c2410_drv_probe,
.remove = ohci_hcd_s3c2410_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_s3c2410_drv_suspend, */
/*.resume = ohci_hcd_s3c2410_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index ce3de106cad..71371de32ad 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -212,10 +212,6 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_sa1111_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_sa1111_suspend, -- tbd */
- /* resume: ohci_sa1111_resume, -- tbd */
-#endif
.stop = ohci_stop,
/*
@@ -235,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index caacf14371f..a2f42a2f47c 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -159,7 +159,7 @@ static const int cc_to_error [16] = {
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
- /* DevNotResp */ -ETIMEDOUT,
+ /* DevNotResp */ -ETIME,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
@@ -388,8 +388,7 @@ struct ohci_hcd {
u32 hc_control; /* copy of hc control reg */
unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */
-
- struct notifier_block reboot_notifier;
+ unsigned autostop:1; /* rh auto stopping/stopped */
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index fa34092bbcd..3a586aab393 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -597,7 +597,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
/* error? retry, until "3 strikes" */
} else if (++ep->error_count >= 3) {
if (status & SL11H_STATMASK_TMOUT)
- urbstat = -ETIMEDOUT;
+ urbstat = -ETIME;
else if (status & SL11H_STATMASK_OVF)
urbstat = -EOVERFLOW;
else
@@ -1517,7 +1517,7 @@ 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 = {
+static const struct file_operations proc_ops = {
.open = proc_sl811h_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -1783,10 +1783,15 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
struct sl811 *sl811 = hcd_to_sl811(hcd);
int retval = 0;
- if (state.event == PM_EVENT_FREEZE)
+ switch (state.event) {
+ case PM_EVENT_FREEZE:
retval = sl811h_bus_suspend(hcd);
- else if (state.event == PM_EVENT_SUSPEND)
+ break;
+ case PM_EVENT_SUSPEND:
+ case PM_EVENT_PRETHAW: /* explicitly discard hw state */
port_power(sl811, 0);
+ break;
+ }
if (retval == 0)
dev->dev.power.power_state = state;
return retval;
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
new file mode 100644
index 00000000000..cb2e2a604d1
--- /dev/null
+++ b/drivers/usb/host/u132-hcd.c
@@ -0,0 +1,3295 @@
+/*
+* Host Controller Driver for the Elan Digital Systems U132 adapter
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.com
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB host drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/pci_ids.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include "../core/hcd.h"
+#include "ohci.h"
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
+ OHCI_INTR_WDH)
+MODULE_AUTHOR("Tony Olech - Elan Digital Systems Limited");
+MODULE_DESCRIPTION("U132 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+INT_MODULE_PARM(testing, 0);
+/* Some boards misreport power switching/overcurrent*/
+static int distrust_firmware = 1;
+module_param(distrust_firmware, bool, 0);
+MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
+ "t setup");
+DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
+/*
+* u132_module_lock exists to protect access to global variables
+*
+*/
+static struct semaphore u132_module_lock;
+static int u132_exiting = 0;
+static int u132_instances = 0;
+static struct list_head u132_static_list;
+/*
+* end of the global variables protected by u132_module_lock
+*/
+static struct workqueue_struct *workqueue;
+#define MAX_U132_PORTS 7
+#define MAX_U132_ADDRS 128
+#define MAX_U132_UDEVS 4
+#define MAX_U132_ENDPS 100
+#define MAX_U132_RINGS 4
+static const char *cc_to_text[16] = {
+ "No Error ",
+ "CRC Error ",
+ "Bit Stuff ",
+ "Data Togg ",
+ "Stall ",
+ "DevNotResp ",
+ "PIDCheck ",
+ "UnExpPID ",
+ "DataOver ",
+ "DataUnder ",
+ "(for hw) ",
+ "(for hw) ",
+ "BufferOver ",
+ "BuffUnder ",
+ "(for HCD) ",
+ "(for HCD) "
+};
+struct u132_port {
+ struct u132 *u132;
+ int reset;
+ int enable;
+ int power;
+ int Status;
+};
+struct u132_addr {
+ u8 address;
+};
+struct u132_udev {
+ struct kref kref;
+ struct usb_device *usb_device;
+ u8 enumeration;
+ u8 udev_number;
+ u8 usb_addr;
+ u8 portnumber;
+ u8 endp_number_in[16];
+ u8 endp_number_out[16];
+};
+#define ENDP_QUEUE_SHIFT 3
+#define ENDP_QUEUE_SIZE (1<<ENDP_QUEUE_SHIFT)
+#define ENDP_QUEUE_MASK (ENDP_QUEUE_SIZE-1)
+struct u132_urbq {
+ struct list_head urb_more;
+ struct urb *urb;
+};
+struct u132_spin {
+ spinlock_t slock;
+};
+struct u132_endp {
+ struct kref kref;
+ u8 udev_number;
+ u8 endp_number;
+ u8 usb_addr;
+ u8 usb_endp;
+ struct u132 *u132;
+ struct list_head endp_ring;
+ struct u132_ring *ring;
+ unsigned toggle_bits:2;
+ unsigned active:1;
+ unsigned delayed:1;
+ unsigned input:1;
+ unsigned output:1;
+ unsigned pipetype:2;
+ unsigned dequeueing:1;
+ unsigned edset_flush:1;
+ unsigned spare_bits:14;
+ unsigned long jiffies;
+ struct usb_host_endpoint *hep;
+ struct u132_spin queue_lock;
+ u16 queue_size;
+ u16 queue_last;
+ u16 queue_next;
+ struct urb *urb_list[ENDP_QUEUE_SIZE];
+ struct list_head urb_more;
+ struct work_struct scheduler;
+};
+struct u132_ring {
+ unsigned in_use:1;
+ unsigned length:7;
+ u8 number;
+ struct u132 *u132;
+ struct u132_endp *curr_endp;
+ struct work_struct scheduler;
+};
+#define OHCI_QUIRK_AMD756 0x01
+#define OHCI_QUIRK_SUPERIO 0x02
+#define OHCI_QUIRK_INITRESET 0x04
+#define OHCI_BIG_ENDIAN 0x08
+#define OHCI_QUIRK_ZFMICRO 0x10
+struct u132 {
+ struct kref kref;
+ struct list_head u132_list;
+ struct semaphore sw_lock;
+ struct semaphore scheduler_lock;
+ struct u132_platform_data *board;
+ struct platform_device *platform_dev;
+ struct u132_ring ring[MAX_U132_RINGS];
+ int sequence_num;
+ int going;
+ int power;
+ int reset;
+ int num_ports;
+ u32 hc_control;
+ u32 hc_fminterval;
+ u32 hc_roothub_status;
+ u32 hc_roothub_a;
+ u32 hc_roothub_portstatus[MAX_ROOT_PORTS];
+ int flags;
+ unsigned long next_statechange;
+ struct work_struct monitor;
+ int num_endpoints;
+ struct u132_addr addr[MAX_U132_ADDRS];
+ struct u132_udev udev[MAX_U132_UDEVS];
+ struct u132_port port[MAX_U132_PORTS];
+ struct u132_endp *endp[MAX_U132_ENDPS];
+};
+int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data);
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, u8 addressofs,
+ u8 width, u32 *data);
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, u8 addressofs,
+ u8 width, u32 data);
+/*
+* these can not be inlines because we need the structure offset!!
+* Does anyone have a better way?????
+*/
+#define u132_read_pcimem(u132, member, data) \
+ usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0, data);
+#define u132_write_pcimem(u132, member, data) \
+ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0, data);
+#define u132_write_pcimem_byte(u132, member, data) \
+ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0x0e, data);
+static inline struct u132 *udev_to_u132(struct u132_udev *udev)
+{
+ u8 udev_number = udev->udev_number;
+ return container_of(udev, struct u132, udev[udev_number]);
+}
+
+static inline struct u132 *hcd_to_u132(struct usb_hcd *hcd)
+{
+ return (struct u132 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *u132_to_hcd(struct u132 *u132)
+{
+ return container_of((void *)u132, struct usb_hcd, hcd_priv);
+}
+
+static inline void u132_disable(struct u132 *u132)
+{
+ u132_to_hcd(u132)->state = HC_STATE_HALT;
+}
+
+
+#define kref_to_u132(d) container_of(d, struct u132, kref)
+#define kref_to_u132_endp(d) container_of(d, struct u132_endp, kref)
+#define kref_to_u132_udev(d) container_of(d, struct u132_udev, kref)
+#include "../misc/usb_u132.h"
+static const char hcd_name[] = "u132_hcd";
+#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | \
+ USB_PORT_STAT_C_SUSPEND | USB_PORT_STAT_C_OVERCURRENT | \
+ USB_PORT_STAT_C_RESET) << 16)
+static void u132_hcd_delete(struct kref *kref)
+{
+ struct u132 *u132 = kref_to_u132(kref);
+ struct platform_device *pdev = u132->platform_dev;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ u132->going += 1;
+ down(&u132_module_lock);
+ list_del_init(&u132->u132_list);
+ u132_instances -= 1;
+ up(&u132_module_lock);
+ dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
+ "2=%p going=%d pdev=%p\n", hcd, u132, u132->going, pdev);
+ usb_put_hcd(hcd);
+}
+
+static inline void u132_u132_put_kref(struct u132 *u132)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static inline void u132_u132_init_kref(struct u132 *u132)
+{
+ kref_init(&u132->kref);
+}
+
+static void u132_udev_delete(struct kref *kref)
+{
+ struct u132_udev *udev = kref_to_u132_udev(kref);
+ udev->udev_number = 0;
+ udev->usb_device = NULL;
+ udev->usb_addr = 0;
+ udev->enumeration = 0;
+}
+
+static inline void u132_udev_put_kref(struct u132 *u132, struct u132_udev *udev)
+{
+ kref_put(&udev->kref, u132_udev_delete);
+}
+
+static inline void u132_udev_get_kref(struct u132 *u132, struct u132_udev *udev)
+{
+ kref_get(&udev->kref);
+}
+
+static inline void u132_udev_init_kref(struct u132 *u132,
+ struct u132_udev *udev)
+{
+ kref_init(&udev->kref);
+}
+
+static inline void u132_ring_put_kref(struct u132 *u132, struct u132_ring *ring)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_ring_requeue_work(struct u132 *u132, struct u132_ring *ring,
+ unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &ring->scheduler, delta))
+ return;
+ } else if (queue_work(workqueue, &ring->scheduler))
+ return;
+ kref_put(&u132->kref, u132_hcd_delete);
+ return;
+}
+
+static void u132_ring_queue_work(struct u132 *u132, struct u132_ring *ring,
+ unsigned int delta)
+{
+ kref_get(&u132->kref);
+ u132_ring_requeue_work(u132, ring, delta);
+ return;
+}
+
+static void u132_ring_cancel_work(struct u132 *u132, struct u132_ring *ring)
+{
+ if (cancel_delayed_work(&ring->scheduler)) {
+ kref_put(&u132->kref, u132_hcd_delete);
+ }
+}
+
+static void u132_endp_delete(struct kref *kref)
+{
+ struct u132_endp *endp = kref_to_u132_endp(kref);
+ struct u132 *u132 = endp->u132;
+ u8 usb_addr = endp->usb_addr;
+ u8 usb_endp = endp->usb_endp;
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ u8 endp_number = endp->endp_number;
+ struct usb_host_endpoint *hep = endp->hep;
+ struct u132_ring *ring = endp->ring;
+ struct list_head *head = &endp->endp_ring;
+ ring->length -= 1;
+ if (endp == ring->curr_endp) {
+ if (list_empty(head)) {
+ ring->curr_endp = NULL;
+ list_del(head);
+ } else {
+ struct u132_endp *next_endp = list_entry(head->next,
+ struct u132_endp, endp_ring);
+ ring->curr_endp = next_endp;
+ list_del(head);
+ }} else
+ list_del(head);
+ if (endp->input) {
+ udev->endp_number_in[usb_endp] = 0;
+ u132_udev_put_kref(u132, udev);
+ }
+ if (endp->output) {
+ udev->endp_number_out[usb_endp] = 0;
+ u132_udev_put_kref(u132, udev);
+ }
+ u132->endp[endp_number - 1] = NULL;
+ hep->hcpriv = NULL;
+ kfree(endp);
+ u132_u132_put_kref(u132);
+}
+
+static inline void u132_endp_put_kref(struct u132 *u132, struct u132_endp *endp)
+{
+ kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_endp_get_kref(struct u132 *u132, struct u132_endp *endp)
+{
+ kref_get(&endp->kref);
+}
+
+static inline void u132_endp_init_kref(struct u132 *u132,
+ struct u132_endp *endp)
+{
+ kref_init(&endp->kref);
+ kref_get(&u132->kref);
+}
+
+static void u132_endp_queue_work(struct u132 *u132, struct u132_endp *endp,
+ unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &endp->scheduler, delta))
+ kref_get(&endp->kref);
+ } else if (queue_work(workqueue, &endp->scheduler))
+ kref_get(&endp->kref);
+ return;
+}
+
+static void u132_endp_cancel_work(struct u132 *u132, struct u132_endp *endp)
+{
+ if (cancel_delayed_work(&endp->scheduler))
+ kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_monitor_put_kref(struct u132 *u132)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_monitor_queue_work(struct u132 *u132, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &u132->monitor, delta)) {
+ kref_get(&u132->kref);
+ }
+ } else if (queue_work(workqueue, &u132->monitor))
+ kref_get(&u132->kref);
+ return;
+}
+
+static void u132_monitor_requeue_work(struct u132 *u132, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &u132->monitor, delta))
+ return;
+ } else if (queue_work(workqueue, &u132->monitor))
+ return;
+ kref_put(&u132->kref, u132_hcd_delete);
+ return;
+}
+
+static void u132_monitor_cancel_work(struct u132 *u132)
+{
+ if (cancel_delayed_work(&u132->monitor))
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static int read_roothub_info(struct u132 *u132)
+{
+ u32 revision;
+ int retval;
+ retval = u132_read_pcimem(u132, revision, &revision);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+ "ntrol\n", retval);
+ return retval;
+ } else if ((revision & 0xFF) == 0x10) {
+ } else if ((revision & 0xFF) == 0x11) {
+ } else {
+ dev_err(&u132->platform_dev->dev, "device revision is not valid"
+ " %08X\n", revision);
+ return -ENODEV;
+ }
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+ "ntrol\n", retval);
+ return retval;
+ }
+ retval = u132_read_pcimem(u132, roothub.status,
+ &u132->hc_roothub_status);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+ "g roothub.status\n", retval);
+ return retval;
+ }
+ retval = u132_read_pcimem(u132, roothub.a, &u132->hc_roothub_a);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+ "g roothub.a\n", retval);
+ return retval;
+ }
+ {
+ int I = u132->num_ports;
+ int i = 0;
+ while (I-- > 0) {
+ retval = u132_read_pcimem(u132, roothub.portstatus[i],
+ &u132->hc_roothub_portstatus[i]);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d acc"
+ "essing device roothub.portstatus[%d]\n"
+ , retval, i);
+ return retval;
+ } else
+ i += 1;
+ }
+ }
+ return 0;
+}
+
+static void u132_hcd_monitor_work(void *data)
+{
+ struct u132 *u132 = data;
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ u132_monitor_put_kref(u132);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ u132_monitor_put_kref(u132);
+ return;
+ } else {
+ int retval;
+ down(&u132->sw_lock);
+ retval = read_roothub_info(u132);
+ if (retval) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ u132_disable(u132);
+ u132->going = 1;
+ up(&u132->sw_lock);
+ usb_hc_died(hcd);
+ ftdi_elan_gone_away(u132->platform_dev);
+ u132_monitor_put_kref(u132);
+ return;
+ } else {
+ u132_monitor_requeue_work(u132, 500);
+ up(&u132->sw_lock);
+ return;
+ }
+ }
+}
+
+static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ struct u132_ring *ring;
+ unsigned long irqs;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ urb->error_count = 0;
+ urb->status = status;
+ urb->hcpriv = NULL;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_next += 1;
+ if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+ urb_more);
+ list_del(next);
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urbq->urb;
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(urbq);
+ } down(&u132->scheduler_lock);
+ ring = endp->ring;
+ ring->in_use = 0;
+ u132_ring_cancel_work(u132, ring);
+ u132_ring_queue_work(u132, ring, 0);
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ usb_hcd_giveback_urb(hcd, urb, NULL);
+ return;
+}
+
+static void u132_hcd_forget_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ u132_endp_put_kref(u132, endp);
+}
+
+static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ unsigned long irqs;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ urb->error_count = 0;
+ urb->status = status;
+ urb->hcpriv = NULL;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_next += 1;
+ if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+ urb_more);
+ list_del(next);
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urbq->urb;
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(urbq);
+ } usb_hcd_giveback_urb(hcd, urb, NULL);
+ return;
+}
+
+static inline int edset_input(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_input(u132->platform_dev, ring->number, endp,
+ urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_setup(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_setup(u132->platform_dev, ring->number, endp,
+ urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_single(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_single(u132->platform_dev, ring->number,
+ endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_output(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_output(u132->platform_dev, ring->number,
+ endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+
+/*
+* must not LOCK sw_lock
+*
+*/
+static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer + urb->actual_length;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length += len;
+ if ((condition_code == TD_CC_NOERROR) &&
+ (urb->transfer_buffer_length > urb->actual_length)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ if (urb->actual_length > 0) {
+ int retval;
+ up(&u132->scheduler_lock);
+ retval = edset_single(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_interrupt_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ } else {
+ ring->in_use = 0;
+ endp->active = 0;
+ endp->jiffies = jiffies +
+ msecs_to_jiffies(urb->interval);
+ u132_ring_cancel_work(u132, ring);
+ u132_ring_queue_work(u132, ring, 0);
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ }
+ return;
+ } else if ((condition_code == TD_DATAUNDERRUN) &&
+ ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ if (condition_code == TD_CC_NOERROR) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 1 & toggle_bits);
+ } else if (condition_code == TD_CC_STALL) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 0);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 0);
+ dev_err(&u132->platform_dev->dev, "urb=%p givin"
+ "g back INTERRUPT %s\n", urb,
+ cc_to_text[condition_code]);
+ }
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ urb->actual_length += len;
+ endp->toggle_bits = toggle_bits;
+ if (urb->transfer_buffer_length > urb->actual_length) {
+ int retval;
+ up(&u132->scheduler_lock);
+ retval = edset_output(u132, ring, endp, urb, address,
+ endp->toggle_bits, u132_hcd_bulk_output_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer + urb->actual_length;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length += len;
+ if ((condition_code == TD_CC_NOERROR) &&
+ (urb->transfer_buffer_length > urb->actual_length)) {
+ int retval;
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, endp->toggle_bits,
+ u132_hcd_bulk_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (condition_code == TD_CC_NOERROR) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else if ((condition_code == TD_DATAUNDERRUN) &&
+ ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else if (condition_code == TD_DATAUNDERRUN) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ dev_warn(&u132->platform_dev->dev, "urb=%p(SHORT NOT OK"
+ ") giving back BULK IN %s\n", urb,
+ cc_to_text[condition_code]);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else if (condition_code == TD_CC_STALL) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+ dev_err(&u132->platform_dev->dev, "urb=%p giving back B"
+ "ULK IN code=%d %s\n", urb, condition_code,
+ cc_to_text[condition_code]);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length = len;
+ if ((condition_code == TD_CC_NOERROR) || ((condition_code ==
+ TD_DATAUNDERRUN) && ((urb->transfer_flags &
+ URB_SHORT_NOT_OK) == 0))) {
+ int retval;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0x3,
+ u132_hcd_configure_empty_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (condition_code == TD_CC_STALL) {
+ up(&u132->scheduler_lock);
+ dev_warn(&u132->platform_dev->dev, "giving back SETUP I"
+ "NPUT STALL urb %p\n", urb);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else {
+ up(&u132->scheduler_lock);
+ dev_err(&u132->platform_dev->dev, "giving back SETUP IN"
+ "PUT %s urb %p\n", cc_to_text[condition_code],
+ urb);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ if (usb_pipein(urb->pipe)) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0,
+ u132_hcd_configure_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0,
+ u132_hcd_configure_empty_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb,
+ u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ u132->addr[0].address = 0;
+ endp->usb_addr = udev->usb_addr;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
+ u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, 0, endp->usb_endp, 0,
+ u132_hcd_enumeration_empty_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length = len;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+ ring->number, endp, urb, address, endp->usb_endp, 0x3,
+ u132_hcd_initial_empty_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address, endp->usb_endp, 0,
+ u132_hcd_initial_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_ring_work_scheduler(void *data);
+static void u132_hcd_endp_work_scheduler(void *data);
+/*
+* this work function is only executed from the work queue
+*
+*/
+static void u132_hcd_ring_work_scheduler(void *data)
+{
+ struct u132_ring *ring = data;
+ struct u132 *u132 = ring->u132;
+ down(&u132->scheduler_lock);
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else if (ring->curr_endp) {
+ struct u132_endp *last_endp = ring->curr_endp;
+ struct list_head *scan;
+ struct list_head *head = &last_endp->endp_ring;
+ unsigned long wakeup = 0;
+ list_for_each(scan, head) {
+ struct u132_endp *endp = list_entry(scan,
+ struct u132_endp, endp_ring);
+ if (endp->queue_next == endp->queue_last) {
+ } else if ((endp->delayed == 0)
+ || time_after_eq(jiffies, endp->jiffies)) {
+ ring->curr_endp = endp;
+ u132_endp_cancel_work(u132, last_endp);
+ u132_endp_queue_work(u132, last_endp, 0);
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else {
+ unsigned long delta = endp->jiffies - jiffies;
+ if (delta > wakeup)
+ wakeup = delta;
+ }
+ }
+ if (last_endp->queue_next == last_endp->queue_last) {
+ } else if ((last_endp->delayed == 0) || time_after_eq(jiffies,
+ last_endp->jiffies)) {
+ u132_endp_cancel_work(u132, last_endp);
+ u132_endp_queue_work(u132, last_endp, 0);
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else {
+ unsigned long delta = last_endp->jiffies - jiffies;
+ if (delta > wakeup)
+ wakeup = delta;
+ }
+ if (wakeup > 0) {
+ u132_ring_requeue_work(u132, ring, wakeup);
+ up(&u132->scheduler_lock);
+ return;
+ } else {
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ }
+ } else {
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ }
+}
+
+static void u132_hcd_endp_work_scheduler(void *data)
+{
+ struct u132_ring *ring;
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ ring = endp->ring;
+ if (endp->edset_flush) {
+ endp->edset_flush = 0;
+ if (endp->dequeueing)
+ usb_ftdi_elan_edset_flush(u132->platform_dev,
+ ring->number, endp);
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->active) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->queue_next == endp->queue_last) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->pipetype == PIPE_INTERRUPT) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_single(u132, ring, endp, urb, address,
+ endp->toggle_bits, u132_hcd_interrupt_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else if (endp->pipetype == PIPE_CONTROL) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (address == 0) {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, address,
+ 0x2, u132_hcd_initial_setup_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (endp->usb_addr == 0) {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, 0, 0x2,
+ u132_hcd_enumeration_address_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ int retval;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, address,
+ 0x2, u132_hcd_configure_setup_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else {
+ if (endp->input) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[
+ ENDP_QUEUE_MASK & endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_input(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_bulk_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ return;
+ }
+ } else { /* output pipe */
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[
+ ENDP_QUEUE_MASK & endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_output(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_bulk_output_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ return;
+ }
+ }
+ }
+}
+
+static void port_power(struct u132 *u132, int pn, int is_on)
+{
+ u132->port[pn].power = is_on;
+}
+
+static void u132_power(struct u132 *u132, int is_on)
+{
+ struct usb_hcd *hcd = u132_to_hcd(u132)
+ ; /* hub is inactive unless the port is powered */
+ if (is_on) {
+ if (u132->power)
+ return;
+ u132->power = 1;
+ hcd->self.controller->power.power_state = PMSG_ON;
+ } else {
+ u132->power = 0;
+ hcd->state = HC_STATE_HALT;
+ hcd->self.controller->power.power_state = PMSG_SUSPEND;
+ }
+}
+
+static int u132_periodic_reinit(struct u132 *u132)
+{
+ int retval;
+ u32 fi = u132->hc_fminterval & 0x03fff;
+ u32 fit;
+ u32 fminterval;
+ retval = u132_read_pcimem(u132, fminterval, &fminterval);
+ if (retval)
+ return retval;
+ fit = fminterval & FIT;
+ retval = u132_write_pcimem(u132, fminterval,
+ (fit ^ FIT) | u132->hc_fminterval);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, periodicstart,
+ ((9 *fi) / 10) & 0x3fff);
+ if (retval)
+ return retval;
+ return 0;
+}
+
+static char *hcfs2string(int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET:
+ return "reset";
+ case OHCI_USB_RESUME:
+ return "resume";
+ case OHCI_USB_OPER:
+ return "operational";
+ case OHCI_USB_SUSPEND:
+ return "suspend";
+ }
+ return "?";
+}
+
+static int u132_usb_reset(struct u132 *u132)
+{
+ int retval;
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval)
+ return retval;
+ u132->hc_control &= OHCI_CTRL_RWC;
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ return 0;
+}
+
+static int u132_init(struct u132 *u132)
+{
+ int retval;
+ u32 control;
+ u132_disable(u132);
+ u132->next_statechange =
+ jiffies; /* SMM owns the HC? not for long! */ {
+ u32 control;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ if (control & OHCI_CTRL_IR) {
+ u32 temp = 50;
+ retval = u132_write_pcimem(u132, intrenable,
+ OHCI_INTR_OC);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem_byte(u132, cmdstatus,
+ OHCI_OCR);
+ if (retval)
+ return retval;
+ check:{
+ retval = u132_read_pcimem(u132, control,
+ &control);
+ if (retval)
+ return retval;
+ }
+ if (control & OHCI_CTRL_IR) {
+ msleep(10);
+ if (--temp == 0) {
+ dev_err(&u132->platform_dev->dev, "USB "
+ "HC takeover failed!(BIOS/SMM b"
+ "ug) control=%08X\n", control);
+ return -EBUSY;
+ }
+ goto check;
+ }
+ u132_usb_reset(u132);
+ }
+ }
+ retval = u132_write_pcimem(u132, intrdisable, OHCI_INTR_MIE);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ if (u132->num_ports == 0) {
+ u32 rh_a = -1;
+ retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+ if (retval)
+ return retval;
+ u132->num_ports = rh_a & RH_A_NDP;
+ retval = read_roothub_info(u132);
+ if (retval)
+ return retval;
+ }
+ if (u132->num_ports > MAX_U132_PORTS) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/* Start an OHCI controller, set the BUS operational
+* resets USB and controller
+* enable interrupts
+*/
+static int u132_run(struct u132 *u132)
+{
+ int retval;
+ u32 control;
+ u32 status;
+ u32 fminterval;
+ u32 periodicstart;
+ u32 cmdstatus;
+ u32 roothub_a;
+ int mask = OHCI_INTR_INIT;
+ int first = u132->hc_fminterval == 0;
+ int sleep_time = 0;
+ int reset_timeout = 30; /* ... allow extra time */
+ u132_disable(u132);
+ if (first) {
+ u32 temp;
+ retval = u132_read_pcimem(u132, fminterval, &temp);
+ if (retval)
+ return retval;
+ u132->hc_fminterval = temp & 0x3fff;
+ if (u132->hc_fminterval != FI) {
+ }
+ u132->hc_fminterval |= FSMP(u132->hc_fminterval) << 16;
+ }
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval)
+ return retval;
+ dev_info(&u132->platform_dev->dev, "resetting from state '%s', control "
+ "= %08X\n", hcfs2string(u132->hc_control & OHCI_CTRL_HCFS),
+ u132->hc_control);
+ switch (u132->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ sleep_time = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_USB_RESUME;
+ sleep_time = 10;
+ break;
+ default:
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_USB_RESET;
+ sleep_time = 50;
+ break;
+ }
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ msleep(sleep_time);
+ retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+ if (retval)
+ return retval;
+ if (!(roothub_a & RH_A_NPS)) {
+ int temp; /* power down each port */
+ for (temp = 0; temp < u132->num_ports; temp++) {
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[temp], RH_PS_LSDA);
+ if (retval)
+ return retval;
+ }
+ }
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ retry:retval = u132_read_pcimem(u132, cmdstatus, &status);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_HCR);
+ if (retval)
+ return retval;
+ extra:{
+ retval = u132_read_pcimem(u132, cmdstatus, &status);
+ if (retval)
+ return retval;
+ if (0 != (status & OHCI_HCR)) {
+ if (--reset_timeout == 0) {
+ dev_err(&u132->platform_dev->dev, "USB HC reset"
+ " timed out!\n");
+ return -ENODEV;
+ } else {
+ msleep(5);
+ goto extra;
+ }
+ }
+ }
+ if (u132->flags & OHCI_QUIRK_INITRESET) {
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ }
+ retval = u132_write_pcimem(u132, ed_controlhead, 0x00000000);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, ed_bulkhead, 0x11000000);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, hcca, 0x00000000);
+ if (retval)
+ return retval;
+ retval = u132_periodic_reinit(u132);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, fminterval, &fminterval);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, periodicstart, &periodicstart);
+ if (retval)
+ return retval;
+ if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
+ if (!(u132->flags & OHCI_QUIRK_INITRESET)) {
+ u132->flags |= OHCI_QUIRK_INITRESET;
+ goto retry;
+ } else
+ dev_err(&u132->platform_dev->dev, "init err(%08x %04x)"
+ "\n", fminterval, periodicstart);
+ } /* start controller operations */
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_BLF);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, cmdstatus, &cmdstatus);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+ retval = u132_write_pcimem(u132, roothub.status, RH_HS_DRWE);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, intrstatus, mask);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, intrdisable,
+ OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
+ OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
+ OHCI_INTR_SO);
+ if (retval)
+ return retval; /* handle root hub init quirks ... */
+ retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+ if (retval)
+ return retval;
+ roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
+ if (u132->flags & OHCI_QUIRK_SUPERIO) {
+ roothub_a |= RH_A_NOCP;
+ roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
+ retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+ if (retval)
+ return retval;
+ } else if ((u132->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
+ roothub_a |= RH_A_NPS;
+ retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+ if (retval)
+ return retval;
+ }
+ retval = u132_write_pcimem(u132, roothub.status, RH_HS_LPSC);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, roothub.b,
+ (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ mdelay((roothub_a >> 23) & 0x1fe);
+ u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+ return 0;
+}
+
+static void u132_hcd_stop(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+ "ed\n", hcd);
+ } else {
+ down(&u132->sw_lock);
+ msleep(100);
+ u132_power(u132, 0);
+ up(&u132->sw_lock);
+ }
+}
+
+static int u132_hcd_start(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else if (hcd->self.controller) {
+ int retval;
+ struct platform_device *pdev =
+ to_platform_device(hcd->self.controller);
+ u16 vendor = ((struct u132_platform_data *)
+ (pdev->dev.platform_data))->vendor;
+ u16 device = ((struct u132_platform_data *)
+ (pdev->dev.platform_data))->device;
+ down(&u132->sw_lock);
+ msleep(10);
+ if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) {
+ u132->flags = OHCI_QUIRK_AMD756;
+ } else if (vendor == PCI_VENDOR_ID_OPTI && device == 0xc861) {
+ dev_err(&u132->platform_dev->dev, "WARNING: OPTi workar"
+ "ounds unavailable\n");
+ } else if (vendor == PCI_VENDOR_ID_COMPAQ && device == 0xa0f8)
+ u132->flags |= OHCI_QUIRK_ZFMICRO;
+ retval = u132_run(u132);
+ if (retval) {
+ u132_disable(u132);
+ u132->going = 1;
+ }
+ msleep(100);
+ up(&u132->sw_lock);
+ return retval;
+ } else {
+ dev_err(&u132->platform_dev->dev, "platform_device missing\n");
+ return -ENODEV;
+ }
+}
+
+static int u132_hcd_reset(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval;
+ down(&u132->sw_lock);
+ retval = u132_init(u132);
+ if (retval) {
+ u132_disable(u132);
+ u132->going = 1;
+ }
+ up(&u132->sw_lock);
+ return retval;
+ }
+}
+
+static int create_endpoint_and_queue_int(struct u132 *u132,
+ struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+ gfp_t mem_flags)
+{
+ struct u132_ring *ring;
+ unsigned long irqs;
+ u8 endp_number = ++u132->num_endpoints;
+ struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+ kmalloc(sizeof(struct u132_endp), mem_flags);
+ if (!endp) {
+ return -ENOMEM;
+ }
+ INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+ spin_lock_init(&endp->queue_lock.slock);
+ INIT_LIST_HEAD(&endp->urb_more);
+ ring = endp->ring = &u132->ring[0];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = hep;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_endp_init_kref(u132, endp);
+ if (usb_pipein(urb->pipe)) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+ endp->input = 1;
+ endp->output = 0;
+ udev->endp_number_in[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+ endp->input = 0;
+ endp->output = 1;
+ udev->endp_number_out[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ }
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->delayed = 1;
+ endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, msecs_to_jiffies(urb->interval));
+ return 0;
+}
+
+static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp, u8 address)
+{
+ urb->hcpriv = u132;
+ endp->delayed = 1;
+ endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+}
+
+static int create_endpoint_and_queue_bulk(struct u132 *u132,
+ struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+ gfp_t mem_flags)
+{
+ int ring_number;
+ struct u132_ring *ring;
+ unsigned long irqs;
+ u8 endp_number = ++u132->num_endpoints;
+ struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+ kmalloc(sizeof(struct u132_endp), mem_flags);
+ if (!endp) {
+ return -ENOMEM;
+ }
+ INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+ spin_lock_init(&endp->queue_lock.slock);
+ INIT_LIST_HEAD(&endp->urb_more);
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = hep;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_endp_init_kref(u132, endp);
+ if (usb_pipein(urb->pipe)) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+ ring_number = 3;
+ endp->input = 1;
+ endp->output = 0;
+ udev->endp_number_in[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+ ring_number = 2;
+ endp->input = 0;
+ endp->output = 1;
+ udev->endp_number_out[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ }
+ ring = endp->ring = &u132->ring[ring_number - 1];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+}
+
+static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp, u8 address)
+{
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+}
+
+static int create_endpoint_and_queue_control(struct u132 *u132,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp,
+ gfp_t mem_flags)
+{
+ struct u132_ring *ring;
+ u8 endp_number = ++u132->num_endpoints;
+ struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+ kmalloc(sizeof(struct u132_endp), mem_flags);
+ if (!endp) {
+ return -ENOMEM;
+ }
+ INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+ spin_lock_init(&endp->queue_lock.slock);
+ INIT_LIST_HEAD(&endp->urb_more);
+ ring = endp->ring = &u132->ring[0];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = hep;
+ u132_endp_init_kref(u132, endp);
+ u132_endp_get_kref(u132, endp);
+ if (usb_addr == 0) {
+ unsigned long irqs;
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->input = 1;
+ endp->output = 1;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_udev_init_kref(u132, udev);
+ u132_udev_get_kref(u132, udev);
+ udev->endp_number_in[usb_endp] = endp_number;
+ udev->endp_number_out[usb_endp] = endp_number;
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ } else { /*(usb_addr > 0) */
+ unsigned long irqs;
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->input = 1;
+ endp->output = 1;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_udev_get_kref(u132, udev);
+ udev->enumeration = 2;
+ udev->endp_number_in[usb_endp] = endp_number;
+ udev->endp_number_out[usb_endp] = endp_number;
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+}
+
+static int queue_control_on_old_endpoint(struct u132 *u132,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp)
+{
+ if (usb_addr == 0) {
+ if (usb_pipein(urb->pipe)) {
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more,
+ &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ } else { /* usb_pipeout(urb->pipe) */
+ struct u132_addr *addr = &u132->addr[usb_dev->devnum];
+ int I = MAX_U132_UDEVS;
+ int i = 0;
+ while (--I > 0) {
+ struct u132_udev *udev = &u132->udev[++i];
+ if (udev->usb_device) {
+ continue;
+ } else {
+ udev->enumeration = 1;
+ u132->addr[0].address = i;
+ endp->udev_number = i;
+ udev->udev_number = i;
+ udev->usb_addr = usb_dev->devnum;
+ u132_udev_init_kref(u132, udev);
+ udev->endp_number_in[usb_endp] =
+ endp->endp_number;
+ u132_udev_get_kref(u132, udev);
+ udev->endp_number_out[usb_endp] =
+ endp->endp_number;
+ udev->usb_device = usb_dev;
+ ((u8 *) (urb->setup_packet))[2] =
+ addr->address = i;
+ u132_udev_get_kref(u132, udev);
+ break;
+ }
+ }
+ if (I == 0) {
+ dev_err(&u132->platform_dev->dev, "run out of d"
+ "evice space\n");
+ return -EINVAL;
+ }
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more,
+ &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ }
+ } else { /*(usb_addr > 0) */
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ urb->hcpriv = u132;
+ if (udev->enumeration == 2) {
+ } else
+ udev->enumeration = 2;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq), GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ }
+}
+
+static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
+ struct urb *urb, gfp_t mem_flags)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (irqs_disabled()) {
+ if (__GFP_WAIT & mem_flags) {
+ printk(KERN_ERR "invalid context for function that migh"
+ "t sleep\n");
+ return -EINVAL;
+ }
+ }
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ return -ESHUTDOWN;
+ } else {
+ u8 usb_addr = usb_pipedevice(urb->pipe);
+ u8 usb_endp = usb_pipeendpoint(urb->pipe);
+ struct usb_device *usb_dev = urb->dev;
+ if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ struct u132_endp *endp = hep->hcpriv;
+ urb->actual_length = 0;
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = queue_int_on_old_endpoint(u132, udev,
+ hep, urb, usb_dev, endp, usb_addr,
+ usb_endp, address);
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp,
+ msecs_to_jiffies(urb->interval))
+ ;
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else { /*(endp == NULL) */
+ return create_endpoint_and_queue_int(u132, udev,
+ hep, urb, usb_dev, usb_addr, usb_endp,
+ address, mem_flags);
+ }
+ } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ dev_err(&u132->platform_dev->dev, "the hardware does no"
+ "t support PIPE_ISOCHRONOUS\n");
+ return -EINVAL;
+ } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ struct u132_endp *endp = hep->hcpriv;
+ urb->actual_length = 0;
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = queue_bulk_on_old_endpoint(u132, udev,
+ hep, urb, usb_dev, endp, usb_addr,
+ usb_endp, address);
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else
+ return create_endpoint_and_queue_bulk(u132,
+ udev, hep, urb, usb_dev, usb_addr,
+ usb_endp, address, mem_flags);
+ } else {
+ struct u132_endp *endp = hep->hcpriv;
+ u16 urb_size = 8;
+ u8 *b = urb->setup_packet;
+ int i = 0;
+ char data[30 *3 + 4];
+ char *d = data;
+ int m = (sizeof(data) - 1) / 3;
+ int l = 0;
+ data[0] = 0;
+ while (urb_size-- > 0) {
+ if (i > m) {
+ } else if (i++ < m) {
+ int w = sprintf(d, " %02X", *b++);
+ d += w;
+ l += w;
+ } else
+ d += sprintf(d, " ..");
+ }
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = queue_control_on_old_endpoint(u132,
+ hep, urb, usb_dev, endp, usb_addr,
+ usb_endp);
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else
+ return create_endpoint_and_queue_control(u132,
+ hep, urb, usb_dev, usb_addr, usb_endp,
+ mem_flags);
+ }
+ }
+}
+
+static int dequeue_from_overflow_chain(struct u132 *u132,
+ struct u132_endp *endp, struct urb *urb)
+{
+ struct list_head *scan;
+ struct list_head *head = &endp->urb_more;
+ list_for_each(scan, head) {
+ struct u132_urbq *urbq = list_entry(scan, struct u132_urbq,
+ urb_more);
+ if (urbq->urb == urb) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ list_del(scan);
+ endp->queue_size -= 1;
+ urb->error_count = 0;
+ urb->hcpriv = NULL;
+ usb_hcd_giveback_urb(hcd, urb, NULL);
+ return 0;
+ } else
+ continue;
+ }
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]=%p ring"
+ "[%d] %c%c usb_endp=%d usb_addr=%d size=%d next=%04X last=%04X"
+ "\n", urb, endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+ endp->usb_endp, endp->usb_addr, endp->queue_size,
+ endp->queue_next, endp->queue_last);
+ return -EINVAL;
+}
+
+static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb)
+{
+ unsigned long irqs;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ if (endp->queue_size == 0) {
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]"
+ "=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb,
+ endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+ endp->usb_endp, endp->usb_addr);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return -EINVAL;
+ }
+ if (urb == endp->urb_list[ENDP_QUEUE_MASK & endp->queue_next]) {
+ if (endp->active) {
+ endp->dequeueing = 1;
+ endp->edset_flush = 1;
+ u132_endp_queue_work(u132, endp, 0);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ urb->hcpriv = NULL;
+ return 0;
+ } else {
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_hcd_abandon_urb(u132, endp, urb, urb->status);
+ return 0;
+ }
+ } else {
+ u16 queue_list = 0;
+ u16 queue_size = endp->queue_size;
+ u16 queue_scan = endp->queue_next;
+ struct urb **urb_slot = NULL;
+ while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+ if (urb == endp->urb_list[ENDP_QUEUE_MASK &
+ ++queue_scan]) {
+ urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+ queue_scan];
+ break;
+ } else
+ continue;
+ }
+ while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+ *urb_slot = endp->urb_list[ENDP_QUEUE_MASK &
+ ++queue_scan];
+ urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+ queue_scan];
+ }
+ if (urb_slot) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ endp->queue_size -= 1;
+ if (list_empty(&endp->urb_more)) {
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next,
+ struct u132_urbq, urb_more);
+ list_del(next);
+ *urb_slot = urbq->urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ kfree(urbq);
+ } urb->error_count = 0;
+ urb->hcpriv = NULL;
+ usb_hcd_giveback_urb(hcd, urb, NULL);
+ return 0;
+ } else if (list_empty(&endp->urb_more)) {
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in "
+ "endp[%d]=%p ring[%d] %c%c usb_endp=%d usb_addr"
+ "=%d size=%d next=%04X last=%04X\n", urb,
+ endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ',
+ endp->output ? 'O' : ' ', endp->usb_endp,
+ endp->usb_addr, endp->queue_size,
+ endp->queue_next, endp->queue_last);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return -EINVAL;
+ } else {
+ int retval = dequeue_from_overflow_chain(u132, endp,
+ urb);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return retval;
+ }
+ }
+}
+
+static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 2) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else {
+ u8 usb_addr = usb_pipedevice(urb->pipe);
+ u8 usb_endp = usb_pipeendpoint(urb->pipe);
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ if (usb_pipein(urb->pipe)) {
+ u8 endp_number = udev->endp_number_in[usb_endp];
+ struct u132_endp *endp = u132->endp[endp_number - 1];
+ return u132_endp_urb_dequeue(u132, endp, urb);
+ } else {
+ u8 endp_number = udev->endp_number_out[usb_endp];
+ struct u132_endp *endp = u132->endp[endp_number - 1];
+ return u132_endp_urb_dequeue(u132, endp, urb);
+ }
+ }
+}
+
+static void u132_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 2) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ } else {
+ struct u132_endp *endp = hep->hcpriv;
+ if (endp)
+ u132_endp_put_kref(u132, endp);
+ }
+}
+
+static int u132_get_frame(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int frame = 0;
+ dev_err(&u132->platform_dev->dev, "TODO: u132_get_frame\n");
+ msleep(100);
+ return frame;
+ }
+}
+
+static int u132_roothub_descriptor(struct u132 *u132,
+ struct usb_hub_descriptor *desc)
+{
+ int retval;
+ u16 temp;
+ u32 rh_a = -1;
+ u32 rh_b = -1;
+ retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+ if (retval)
+ return retval;
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = (rh_a & RH_A_POTPGT) >> 24;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = u132->num_ports;
+ temp = 1 + (u132->num_ports / 8);
+ desc->bDescLength = 7 + 2 *temp;
+ temp = 0;
+ if (rh_a & RH_A_NPS)
+ temp |= 0x0002;
+ if (rh_a & RH_A_PSM)
+ temp |= 0x0001;
+ if (rh_a & RH_A_NOCP) {
+ temp |= 0x0010;
+ } else if (rh_a & RH_A_OCPM)
+ temp |= 0x0008;
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+ retval = u132_read_pcimem(u132, roothub.b, &rh_b);
+ if (retval)
+ return retval;
+ memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
+ desc->bitmap[0] = rh_b & RH_B_DR;
+ if (u132->num_ports > 7) {
+ desc->bitmap[1] = (rh_b & RH_B_DR) >> 8;
+ desc->bitmap[2] = 0xff;
+ } else
+ desc->bitmap[1] = 0xff;
+ return 0;
+}
+
+static int u132_roothub_status(struct u132 *u132, __le32 *desc)
+{
+ u32 rh_status = -1;
+ int ret_status = u132_read_pcimem(u132, roothub.status, &rh_status);
+ *desc = cpu_to_le32(rh_status);
+ return ret_status;
+}
+
+static int u132_roothub_portstatus(struct u132 *u132, __le32 *desc, u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int port = wIndex - 1;
+ u32 rh_portstatus = -1;
+ int ret_portstatus = u132_read_pcimem(u132,
+ roothub.portstatus[port], &rh_portstatus);
+ *desc = cpu_to_le32(rh_portstatus);
+ if (*(u16 *) (desc + 2)) {
+ dev_info(&u132->platform_dev->dev, "Port %d Status Chan"
+ "ge = %08X\n", port, *desc);
+ }
+ return ret_portstatus;
+ }
+}
+
+
+/* this timer value might be vendor-specific ... */
+#define PORT_RESET_HW_MSEC 10
+#define PORT_RESET_MSEC 10
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1, t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
+static int u132_roothub_portreset(struct u132 *u132, int port_index)
+{
+ int retval;
+ u32 fmnumber;
+ u16 now;
+ u16 reset_done;
+ retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+ if (retval)
+ return retval;
+ now = fmnumber;
+ reset_done = now + PORT_RESET_MSEC;
+ do {
+ u32 portstat;
+ do {
+ retval = u132_read_pcimem(u132,
+ roothub.portstatus[port_index], &portstat);
+ if (retval)
+ return retval;
+ if (RH_PS_PRS & portstat) {
+ continue;
+ } else
+ break;
+ } while (tick_before(now, reset_done));
+ if (RH_PS_PRS & portstat)
+ return -ENODEV;
+ if (RH_PS_CCS & portstat) {
+ if (RH_PS_PRSC & portstat) {
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index],
+ RH_PS_PRSC);
+ if (retval)
+ return retval;
+ }
+ } else
+ break; /* start the next reset,
+ sleep till it's probably done */
+ retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+ RH_PS_PRS);
+ if (retval)
+ return retval;
+ msleep(PORT_RESET_HW_MSEC);
+ retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+ if (retval)
+ return retval;
+ now = fmnumber;
+ } while (tick_before(now, reset_done));
+ return 0;
+}
+
+static int u132_roothub_setportfeature(struct u132 *u132, u16 wValue,
+ u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int retval;
+ int port_index = wIndex - 1;
+ struct u132_port *port = &u132->port[port_index];
+ port->Status &= ~(1 << wValue);
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index], RH_PS_PSS);
+ if (retval)
+ return retval;
+ return 0;
+ case USB_PORT_FEAT_POWER:
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index], RH_PS_PPS);
+ if (retval)
+ return retval;
+ return 0;
+ case USB_PORT_FEAT_RESET:
+ retval = u132_roothub_portreset(u132, port_index);
+ if (retval)
+ return retval;
+ return 0;
+ default:
+ return -EPIPE;
+ }
+ }
+}
+
+static int u132_roothub_clearportfeature(struct u132 *u132, u16 wValue,
+ u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int port_index = wIndex - 1;
+ u32 temp;
+ int retval;
+ struct u132_port *port = &u132->port[port_index];
+ port->Status &= ~(1 << wValue);
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ temp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ temp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ temp = RH_PS_POCI;
+ if ((u132->hc_control & OHCI_CTRL_HCFS)
+ != OHCI_USB_OPER) {
+ dev_err(&u132->platform_dev->dev, "TODO resume_"
+ "root_hub\n");
+ }
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ temp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ temp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ temp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ temp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ temp = RH_PS_PRSC;
+ break;
+ default:
+ return -EPIPE;
+ }
+ retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+ temp);
+ if (retval)
+ return retval;
+ return 0;
+ }
+}
+
+
+/* the virtual root hub timer IRQ checks for hub status*/
+static int u132_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p has been remov"
+ "ed %d\n", hcd, u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+ "ed\n", hcd);
+ dump_stack();
+ return -ESHUTDOWN;
+ } else {
+ int i, changed = 0, length = 1;
+ if (u132->flags & OHCI_QUIRK_AMD756) {
+ if ((u132->hc_roothub_a & RH_A_NDP) > MAX_ROOT_PORTS) {
+ dev_err(&u132->platform_dev->dev, "bogus NDP, r"
+ "ereads as NDP=%d\n",
+ u132->hc_roothub_a & RH_A_NDP);
+ goto done;
+ }
+ }
+ if (u132->hc_roothub_status & (RH_HS_LPSC | RH_HS_OCIC)) {
+ buf[0] = changed = 1;
+ } else
+ buf[0] = 0;
+ if (u132->num_ports > 7) {
+ buf[1] = 0;
+ length++;
+ }
+ for (i = 0; i < u132->num_ports; i++) {
+ if (u132->hc_roothub_portstatus[i] & (RH_PS_CSC |
+ RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC |
+ RH_PS_PRSC)) {
+ changed = 1;
+ if (i < 7) {
+ buf[0] |= 1 << (i + 1);
+ } else
+ buf[1] |= 1 << (i - 7);
+ continue;
+ }
+ if (!(u132->hc_roothub_portstatus[i] & RH_PS_CCS)) {
+ continue;
+ }
+ if ((u132->hc_roothub_portstatus[i] & RH_PS_PSS)) {
+ continue;
+ }
+ }
+ done:return changed ? length : 0;
+ }
+}
+
+static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ down(&u132->sw_lock);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto stall;
+ }
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto stall;
+ }
+ break;
+ case ClearPortFeature:{
+ retval = u132_roothub_clearportfeature(u132,
+ wValue, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetHubDescriptor:{
+ retval = u132_roothub_descriptor(u132,
+ (struct usb_hub_descriptor *)buf);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetHubStatus:{
+ retval = u132_roothub_status(u132,
+ (__le32 *) buf);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetPortStatus:{
+ retval = u132_roothub_portstatus(u132,
+ (__le32 *) buf, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ case SetPortFeature:{
+ retval = u132_roothub_setportfeature(u132,
+ wValue, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ default:
+ goto stall;
+ error:u132_disable(u132);
+ u132->going = 1;
+ break;
+ stall:retval = -EPIPE;
+ break;
+ }
+ up(&u132->sw_lock);
+ return retval;
+ }
+}
+
+static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static void u132_hub_irq_enable(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ } else if (u132->going > 0)
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+}
+
+
+#ifdef CONFIG_PM
+static int u132_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static int u132_hcd_resume(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static int u132_bus_suspend(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static int u132_bus_resume(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+#else
+#define u132_hcd_suspend NULL
+#define u132_hcd_resume NULL
+#define u132_bus_suspend NULL
+#define u132_bus_resume NULL
+#endif
+static struct hc_driver u132_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct u132),
+ .irq = NULL,
+ .flags = HCD_USB11 | HCD_MEMORY,
+ .reset = u132_hcd_reset,
+ .start = u132_hcd_start,
+ .suspend = u132_hcd_suspend,
+ .resume = u132_hcd_resume,
+ .stop = u132_hcd_stop,
+ .urb_enqueue = u132_urb_enqueue,
+ .urb_dequeue = u132_urb_dequeue,
+ .endpoint_disable = u132_endpoint_disable,
+ .get_frame_number = u132_get_frame,
+ .hub_status_data = u132_hub_status_data,
+ .hub_control = u132_hub_control,
+ .bus_suspend = u132_bus_suspend,
+ .bus_resume = u132_bus_resume,
+ .start_port_reset = u132_start_port_reset,
+ .hub_irq_enable = u132_hub_irq_enable,
+};
+
+/*
+* This function may be called by the USB core whilst the "usb_all_devices_rwsem"
+* is held for writing, thus this module must not call usb_remove_hcd()
+* synchronously - but instead should immediately stop activity to the
+* device and ansynchronously call usb_remove_hcd()
+*/
+static int __devexit u132_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ if (hcd) {
+ struct u132 *u132 = hcd_to_u132(hcd);
+ dump_stack();
+ if (u132->going++ > 1) {
+ return -ENODEV;
+ } else {
+ int rings = MAX_U132_RINGS;
+ int endps = MAX_U132_ENDPS;
+ msleep(100);
+ down(&u132->sw_lock);
+ u132_monitor_cancel_work(u132);
+ while (rings-- > 0) {
+ struct u132_ring *ring = &u132->ring[rings];
+ u132_ring_cancel_work(u132, ring);
+ } while (endps-- > 0) {
+ struct u132_endp *endp = u132->endp[endps];
+ if (endp)
+ u132_endp_cancel_work(u132, endp);
+ }
+ u132->going += 1;
+ printk(KERN_INFO "removing device u132.%d\n",
+ u132->sequence_num);
+ up(&u132->sw_lock);
+ usb_remove_hcd(hcd);
+ u132_u132_put_kref(u132);
+ return 0;
+ }
+ } else
+ return 0;
+}
+
+static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
+{
+ int rings = MAX_U132_RINGS;
+ int ports = MAX_U132_PORTS;
+ int addrs = MAX_U132_ADDRS;
+ int udevs = MAX_U132_UDEVS;
+ int endps = MAX_U132_ENDPS;
+ u132->board = pdev->dev.platform_data;
+ u132->platform_dev = pdev;
+ u132->power = 0;
+ u132->reset = 0;
+ init_MUTEX(&u132->sw_lock);
+ init_MUTEX(&u132->scheduler_lock);
+ while (rings-- > 0) {
+ struct u132_ring *ring = &u132->ring[rings];
+ ring->u132 = u132;
+ ring->number = rings + 1;
+ ring->length = 0;
+ ring->curr_endp = NULL;
+ INIT_WORK(&ring->scheduler, u132_hcd_ring_work_scheduler,
+ (void *)ring);
+ } down(&u132->sw_lock);
+ INIT_WORK(&u132->monitor, u132_hcd_monitor_work, (void *)u132);
+ while (ports-- > 0) {
+ struct u132_port *port = &u132->port[ports];
+ port->u132 = u132;
+ port->reset = 0;
+ port->enable = 0;
+ port->power = 0;
+ port->Status = 0;
+ } while (addrs-- > 0) {
+ struct u132_addr *addr = &u132->addr[addrs];
+ addr->address = 0;
+ } while (udevs-- > 0) {
+ struct u132_udev *udev = &u132->udev[udevs];
+ int i = ARRAY_SIZE(udev->endp_number_in);
+ int o = ARRAY_SIZE(udev->endp_number_out);
+ udev->usb_device = NULL;
+ udev->udev_number = 0;
+ udev->usb_addr = 0;
+ udev->portnumber = 0;
+ while (i-- > 0) {
+ udev->endp_number_in[i] = 0;
+ }
+ while (o-- > 0) {
+ udev->endp_number_out[o] = 0;
+ }
+ }
+ while (endps-- > 0) {
+ u132->endp[endps] = NULL;
+ }
+ up(&u132->sw_lock);
+ return;
+}
+
+static int __devinit u132_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ msleep(100);
+ if (u132_exiting > 0) {
+ return -ENODEV;
+ } /* refuse to confuse usbcore */
+ if (pdev->dev.dma_mask) {
+ return -EINVAL;
+ }
+ hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
+ );
+ ftdi_elan_gone_away(pdev);
+ return -ENOMEM;
+ } else {
+ int retval = 0;
+ struct u132 *u132 = hcd_to_u132(hcd);
+ hcd->rsrc_start = 0;
+ down(&u132_module_lock);
+ list_add_tail(&u132->u132_list, &u132_static_list);
+ u132->sequence_num = ++u132_instances;
+ up(&u132_module_lock);
+ u132_u132_init_kref(u132);
+ u132_initialise(u132, pdev);
+ hcd->product_desc = "ELAN U132 Host Controller";
+ retval = usb_add_hcd(hcd, 0, 0);
+ if (retval != 0) {
+ dev_err(&u132->platform_dev->dev, "init error %d\n",
+ retval);
+ u132_u132_put_kref(u132);
+ return retval;
+ } else {
+ u132_monitor_queue_work(u132, 100);
+ return 0;
+ }
+ }
+}
+
+
+#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 u132_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ if (state.event == PM_EVENT_FREEZE) {
+ retval = u132_bus_suspend(hcd);
+ } else if (state.event == PM_EVENT_SUSPEND) {
+ int ports = MAX_U132_PORTS;
+ while (ports-- > 0) {
+ port_power(u132, ports, 0);
+ }
+ }
+ if (retval == 0)
+ pdev->dev.power.power_state = state;
+ return retval;
+ }
+}
+
+static int u132_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+ int ports = MAX_U132_PORTS;
+ while (ports-- > 0) {
+ port_power(u132, ports, 1);
+ }
+ retval = 0;
+ } else {
+ pdev->dev.power.power_state = PMSG_ON;
+ retval = u132_bus_resume(hcd);
+ }
+ return retval;
+ }
+}
+
+#else
+#define u132_suspend NULL
+#define u132_resume NULL
+#endif
+/*
+* this driver is loaded explicitely by ftdi_u132
+*
+* the platform_driver struct is static because it is per type of module
+*/
+static struct platform_driver u132_platform_driver = {
+ .probe = u132_probe,
+ .remove = __devexit_p(u132_remove),
+ .suspend = u132_suspend,
+ .resume = u132_resume,
+ .driver = {
+ .name = (char *)hcd_name,
+ .owner = THIS_MODULE,
+ },
+};
+static int __init u132_hcd_init(void)
+{
+ int retval;
+ INIT_LIST_HEAD(&u132_static_list);
+ u132_instances = 0;
+ u132_exiting = 0;
+ init_MUTEX(&u132_module_lock);
+ if (usb_disabled())
+ return -ENODEV;
+ printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__,
+ __DATE__);
+ workqueue = create_singlethread_workqueue("u132");
+ retval = platform_driver_register(&u132_platform_driver);
+ return retval;
+}
+
+
+module_init(u132_hcd_init);
+static void __exit u132_hcd_exit(void)
+{
+ struct u132 *u132;
+ struct u132 *temp;
+ down(&u132_module_lock);
+ u132_exiting += 1;
+ up(&u132_module_lock);
+ list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
+ platform_device_unregister(u132->platform_dev);
+ } platform_driver_unregister(&u132_platform_driver);
+ printk(KERN_INFO "u132-hcd driver deregistered\n");
+ wait_event(u132_hcd_wait, u132_instances == 0);
+ flush_workqueue(workqueue);
+ destroy_workqueue(workqueue);
+}
+
+
+module_exit(u132_hcd_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index dc286a48caf..e345f15b7d8 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -16,7 +16,7 @@
#include "uhci-hcd.h"
-#define uhci_debug_operations (* (struct file_operations *) NULL)
+#define uhci_debug_operations (* (const struct file_operations *) NULL)
static struct dentry *uhci_debugfs_root;
#ifdef DEBUG
@@ -428,7 +428,7 @@ struct uhci_debug {
static int uhci_debug_open(struct inode *inode, struct file *file)
{
- struct uhci_hcd *uhci = inode->u.generic_ip;
+ struct uhci_hcd *uhci = inode->i_private;
struct uhci_debug *up;
int ret = -ENOMEM;
unsigned long flags;
@@ -500,7 +500,7 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
}
#undef uhci_debug_operations
-static struct file_operations uhci_debug_operations = {
+static const struct file_operations uhci_debug_operations = {
.owner = THIS_MODULE,
.open = uhci_debug_open,
.llseek = uhci_debug_lseek,
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 4151f618602..eb4eab98e8b 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -734,6 +734,10 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
/* FIXME: Enable non-PME# remote wakeup? */
+ /* make sure snapshot being resumed re-enumerates everything */
+ if (message.event == PM_EVENT_PRETHAW)
+ uhci_hc_died(uhci);
+
done_okay:
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
done:
@@ -909,8 +913,7 @@ static int __init uhci_hcd_init(void)
return 0;
init_failed:
- if (kmem_cache_destroy(uhci_up_cachep))
- warn("not all urb_privs were freed!");
+ kmem_cache_destroy(uhci_up_cachep);
up_failed:
debugfs_remove(uhci_debugfs_root);
@@ -926,10 +929,7 @@ errbuf_failed:
static void __exit uhci_hcd_cleanup(void)
{
pci_unregister_driver(&uhci_pci_driver);
-
- if (kmem_cache_destroy(uhci_up_cachep))
- warn("not all urb_privs were freed!");
-
+ kmem_cache_destroy(uhci_up_cachep);
debugfs_remove(uhci_debugfs_root);
kfree(errbuf);
}
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index c545ef92fe2..16fb72eb6fc 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -84,6 +84,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
unsigned long port_addr)
{
int status;
+ int i;
if (inw(port_addr) & (USBPORTSC_SUSP | USBPORTSC_RD)) {
CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
@@ -92,9 +93,14 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
/* The controller won't actually turn off the RD bit until
* it has had a chance to send a low-speed EOP sequence,
- * which takes 3 bit times (= 2 microseconds). We'll delay
- * slightly longer for good luck. */
- udelay(4);
+ * which is supposed to take 3 bit times (= 2 microseconds).
+ * Experiments show that some controllers take longer, so
+ * we'll poll for completion. */
+ for (i = 0; i < 10; ++i) {
+ if (!(inw(port_addr) & USBPORTSC_RD))
+ break;
+ udelay(1);
+ }
}
clear_bit(port, &uhci->resuming_ports);
}
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 08daf400f98..ca6305c1d64 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -424,7 +424,7 @@ static void mdc800_usb_download_notify (struct urb *urb, struct pt_regs *res)
***************************************************************************/
static struct usb_driver mdc800_usb_driver;
-static struct file_operations mdc800_device_ops;
+static const struct file_operations mdc800_device_ops;
static struct usb_class_driver mdc800_class = {
.name = "mdc800%d",
.fops = &mdc800_device_ops,
@@ -941,7 +941,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
****************************************************************************/
/* File Operations of this drivers */
-static struct file_operations mdc800_device_ops =
+static const struct file_operations mdc800_device_ops =
{
.owner = THIS_MODULE,
.read = mdc800_device_read,
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index b2bafc37c41..5f861331932 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -225,7 +225,7 @@ static inline void mts_debug_dump(struct mts_desc* desc) {
}
-static inline void mts_show_command(Scsi_Cmnd *srb)
+static inline void mts_show_command(struct scsi_cmnd *srb)
{
char *what = NULL;
@@ -309,7 +309,7 @@ static inline void mts_show_command(Scsi_Cmnd *srb)
#else
-static inline void mts_show_command(Scsi_Cmnd * dummy)
+static inline void mts_show_command(struct scsi_cmnd * dummy)
{
}
@@ -338,7 +338,7 @@ static int mts_slave_configure (struct scsi_device *s)
return 0;
}
-static int mts_scsi_abort (Scsi_Cmnd *srb)
+static int mts_scsi_abort(struct scsi_cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
@@ -349,7 +349,7 @@ static int mts_scsi_abort (Scsi_Cmnd *srb)
return FAILED;
}
-static int mts_scsi_host_reset (Scsi_Cmnd *srb)
+static int mts_scsi_host_reset(struct scsi_cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int result, rc;
@@ -366,8 +366,8 @@ static int mts_scsi_host_reset (Scsi_Cmnd *srb)
return result ? FAILED : SUCCESS;
}
-static
-int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
+static int
+mts_scsi_queuecommand(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback);
static void mts_transfer_cleanup( struct urb *transfer );
static void mts_do_sg(struct urb * transfer, struct pt_regs *regs);
@@ -537,7 +537,7 @@ static const unsigned char mts_direction[256/8] = {
#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
static void
-mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
+mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc)
{
int pipe;
struct scatterlist * sg;
@@ -588,8 +588,8 @@ mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
}
-static
-int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
+static int
+mts_scsi_queuecommand(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int err = 0;
diff --git a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h
index 926d4bdc674..d5d62a93905 100644
--- a/drivers/usb/image/microtek.h
+++ b/drivers/usb/image/microtek.h
@@ -8,14 +8,14 @@
*
*/
-typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *);
+typedef void (*mts_scsi_cmnd_callback)(struct scsi_cmnd *);
struct mts_transfer_context
{
struct mts_desc* instance;
mts_scsi_cmnd_callback final_callback;
- Scsi_Cmnd *srb;
+ struct scsi_cmnd *srb;
void* data;
unsigned data_length;
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
index 650103bc961..a102a58fe36 100644
--- a/drivers/usb/input/Kconfig
+++ b/drivers/usb/input/Kconfig
@@ -205,10 +205,12 @@ config USB_TOUCHSCREEN
depends on USB && INPUT
---help---
USB Touchscreen driver for:
- - eGalax Touchkit USB
+ - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
- PanJit TouchSet USB
- - 3M MicroTouch USB
+ - 3M MicroTouch USB (EX II series)
- ITM
+ - some other eTurboTouch
+ - Gunze AHL61
Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff.
@@ -218,7 +220,7 @@ config USB_TOUCHSCREEN
config USB_TOUCHSCREEN_EGALAX
default y
- bool "eGalax device support" if EMBEDDED
+ bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_PANJIT
@@ -228,7 +230,7 @@ config USB_TOUCHSCREEN_PANJIT
config USB_TOUCHSCREEN_3M
default y
- bool "3M/Microtouch device support" if EMBEDDED
+ bool "3M/Microtouch EX II series device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_ITM
@@ -236,6 +238,16 @@ config USB_TOUCHSCREEN_ITM
bool "ITM device support" if EMBEDDED
depends on USB_TOUCHSCREEN
+config USB_TOUCHSCREEN_ETURBO
+ default y
+ bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
+ depends on USB_TOUCHSCREEN
+
+config USB_TOUCHSCREEN_GUNZE
+ default y
+ bool "Gunze AHL61 device support" if EMBEDDED
+ depends on USB_TOUCHSCREEN
+
config USB_YEALINK
tristate "Yealink usb-p1k voip phone"
depends on USB && INPUT && EXPERIMENTAL
@@ -326,3 +338,13 @@ config USB_APPLETOUCH
To compile this driver as a module, choose M here: the
module will be called appletouch.
+
+config USB_TRANCEVIBRATOR
+ tristate "PlayStation 2 Trance Vibrator driver support"
+ depends on USB
+ help
+ Say Y here if you want to connect a PlayStation 2 Trance Vibrator
+ device to your computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called trancevibrator.
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
index 764114529c5..48551be324a 100644
--- a/drivers/usb/input/Makefile
+++ b/drivers/usb/input/Makefile
@@ -3,6 +3,7 @@
#
# Multipart objects.
+wacom-objs := wacom_sys.o wacom_wac.o
usbhid-objs := hid-core.o
# Optional parts of multipart objects.
@@ -44,6 +45,7 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o
obj-$(CONFIG_USB_YEALINK) += yealink.o
obj-$(CONFIG_USB_XPAD) += xpad.o
obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o
+obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/usb/input/acecad.c b/drivers/usb/input/acecad.c
index 18c10e150ef..d83603ba40a 100644
--- a/drivers/usb/input/acecad.c
+++ b/drivers/usb/input/acecad.c
@@ -141,10 +141,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_
endpoint = &interface->endpoint[0].desc;
- if (!(endpoint->bEndpointAddress & 0x80))
- return -ENODEV;
-
- if ((endpoint->bmAttributes & 3) != 3)
+ if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
diff --git a/drivers/usb/input/appletouch.c b/drivers/usb/input/appletouch.c
index 044faa07e29..0aa9cc2bfd6 100644
--- a/drivers/usb/input/appletouch.c
+++ b/drivers/usb/input/appletouch.c
@@ -436,10 +436,7 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
iface_desc = iface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
- if (!int_in_endpointAddr &&
- (endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT)) {
+ if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
break;
diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
index 3719fcb04b8..3558d7ed99b 100644
--- a/drivers/usb/input/ati_remote.c
+++ b/drivers/usb/input/ati_remote.c
@@ -732,12 +732,8 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
endpoint_in = &iface_host->endpoint[0].desc;
endpoint_out = &iface_host->endpoint[1].desc;
- if (!(endpoint_in->bEndpointAddress & USB_DIR_IN)) {
- err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__);
- return -ENODEV;
- }
- if ((endpoint_in->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) {
- err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__);
+ if (!usb_endpoint_is_int_in(endpoint_in)) {
+ err("%s: Unexpected endpoint_in\n", __FUNCTION__);
return -ENODEV;
}
if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index 3305fb6079e..81b1ea01a17 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -1023,7 +1023,8 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
return;
case -EILSEQ: /* protocol error or unplug */
case -EPROTO: /* protocol error or unplug */
- case -ETIMEDOUT: /* NAK */
+ case -ETIME: /* protocol error or unplug */
+ case -ETIMEDOUT: /* Should never happen, but... */
clear_bit(HID_IN_RUNNING, &hid->iofl);
hid_io_error(hid);
return;
@@ -1535,13 +1536,17 @@ void hid_init_reports(struct hid_device *hid)
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
-#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677
@@ -1591,6 +1596,13 @@ void hid_init_reports(struct hid_device *hid)
#define USB_VENDOR_ID_YEALINK 0x6993
#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
+
+#define USB_VENDOR_ID_ALCOR 0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
+
+#define USB_VENDOR_ID_SUN 0x0430
+#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
+
/*
* Alphabetically sorted blacklist by quirk type.
*/
@@ -1608,6 +1620,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
@@ -1620,9 +1633,12 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
@@ -1690,7 +1706,11 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
@@ -1701,6 +1721,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
@@ -1711,6 +1732,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
index f6b839c257a..a2b419d1374 100644
--- a/drivers/usb/input/hiddev.c
+++ b/drivers/usb/input/hiddev.c
@@ -722,7 +722,7 @@ inval:
return -EINVAL;
}
-static struct file_operations hiddev_fops = {
+static const struct file_operations hiddev_fops = {
.owner = THIS_MODULE,
.read = hiddev_read,
.write = hiddev_write,
diff --git a/drivers/usb/input/itmtouch.c b/drivers/usb/input/itmtouch.c
index 86acb5f1907..61966d719ca 100644
--- a/drivers/usb/input/itmtouch.c
+++ b/drivers/usb/input/itmtouch.c
@@ -87,7 +87,7 @@ static void itmtouch_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
diff --git a/drivers/usb/input/keyspan_remote.c b/drivers/usb/input/keyspan_remote.c
index 4723b310f27..a9035955157 100644
--- a/drivers/usb/input/keyspan_remote.c
+++ b/drivers/usb/input/keyspan_remote.c
@@ -420,8 +420,7 @@ static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_i
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
endpoint = &iface->endpoint[i].desc;
- if ((endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ if (usb_endpoint_is_int_in(endpoint)) {
/* we found our interrupt in endpoint */
return endpoint;
}
diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c
index a9ccda8810e..5dce951f275 100644
--- a/drivers/usb/input/mtouchusb.c
+++ b/drivers/usb/input/mtouchusb.c
@@ -107,7 +107,7 @@ static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c
index b3c0d0c3eae..f0f8db6810a 100644
--- a/drivers/usb/input/powermate.c
+++ b/drivers/usb/input/powermate.c
@@ -313,9 +313,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
- if (!(endpoint->bEndpointAddress & 0x80))
- return -EIO;
- if ((endpoint->bmAttributes & 3) != 3)
+ if (!usb_endpoint_is_int_in(endpoint))
return -EIO;
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c
index 0149043ffb9..30b9f820e7a 100644
--- a/drivers/usb/input/touchkitusb.c
+++ b/drivers/usb/input/touchkitusb.c
@@ -201,7 +201,7 @@ static void touchkit_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
diff --git a/drivers/usb/input/trancevibrator.c b/drivers/usb/input/trancevibrator.c
new file mode 100644
index 00000000000..33cd91d11ec
--- /dev/null
+++ b/drivers/usb/input/trancevibrator.c
@@ -0,0 +1,159 @@
+/*
+ * PlayStation 2 Trance Vibrator driver
+ *
+ * Copyright (C) 2006 Sam Hocevar <sam@zoy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Standard include files */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v1.1"
+#define DRIVER_AUTHOR "Sam Hocevar, sam@zoy.org"
+#define DRIVER_DESC "PlayStation 2 Trance Vibrator driver"
+
+#define TRANCEVIBRATOR_VENDOR_ID 0x0b49 /* ASCII Corporation */
+#define TRANCEVIBRATOR_PRODUCT_ID 0x064f /* Trance Vibrator */
+
+static struct usb_device_id id_table [] = {
+ { USB_DEVICE(TRANCEVIBRATOR_VENDOR_ID, TRANCEVIBRATOR_PRODUCT_ID) },
+ { },
+};
+MODULE_DEVICE_TABLE (usb, id_table);
+
+/* Driver-local specific stuff */
+struct trancevibrator {
+ struct usb_device *udev;
+ unsigned int speed;
+};
+
+static ssize_t show_speed(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct trancevibrator *tv = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d\n", tv->speed);
+}
+
+static ssize_t set_speed(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct trancevibrator *tv = usb_get_intfdata(intf);
+ int temp, retval;
+
+ temp = simple_strtoul(buf, NULL, 10);
+ if (temp > 255)
+ temp = 255;
+ else if (temp < 0)
+ temp = 0;
+ tv->speed = temp;
+
+ dev_dbg(&tv->udev->dev, "speed = %d\n", tv->speed);
+
+ /* Set speed */
+ retval = usb_control_msg(tv->udev, usb_sndctrlpipe(tv->udev, 0),
+ 0x01, /* vendor request: set speed */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ tv->speed, /* speed value */
+ 0, NULL, 0, USB_CTRL_GET_TIMEOUT);
+ if (retval) {
+ dev_dbg(&tv->udev->dev, "retval = %d\n", retval);
+ return retval;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(speed, S_IWUGO | S_IRUGO, show_speed, set_speed);
+
+static int tv_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct trancevibrator *dev;
+ int retval;
+
+ dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dev->udev = usb_get_dev(udev);
+ usb_set_intfdata(interface, dev);
+ retval = device_create_file(&interface->dev, &dev_attr_speed);
+ if (retval)
+ goto error_create_file;
+
+ return 0;
+
+error_create_file:
+ usb_put_dev(udev);
+ usb_set_intfdata(interface, NULL);
+error:
+ kfree(dev);
+ return retval;
+}
+
+static void tv_disconnect(struct usb_interface *interface)
+{
+ struct trancevibrator *dev;
+
+ dev = usb_get_intfdata (interface);
+ usb_set_intfdata(interface, NULL);
+ device_remove_file(&interface->dev, &dev_attr_speed);
+ usb_put_dev(dev->udev);
+ kfree(dev);
+}
+
+/* USB subsystem object */
+static struct usb_driver tv_driver = {
+ .name = "trancevibrator",
+ .probe = tv_probe,
+ .disconnect = tv_disconnect,
+ .id_table = id_table,
+};
+
+static int __init tv_init(void)
+{
+ int retval = usb_register(&tv_driver);
+ if (retval) {
+ err("usb_register failed. Error number %d", retval);
+ return retval;
+ }
+
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return 0;
+}
+
+static void __exit tv_exit(void)
+{
+ usb_deregister(&tv_driver);
+}
+
+module_init (tv_init);
+module_exit (tv_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c
index 446935b671d..0fb792be95e 100644
--- a/drivers/usb/input/usbmouse.c
+++ b/drivers/usb/input/usbmouse.c
@@ -218,7 +218,7 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(3, 1, 2) },
- { } /* Terminating entry */
+ { } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
diff --git a/drivers/usb/input/usbtouchscreen.c b/drivers/usb/input/usbtouchscreen.c
index a338bf4c2d7..4640d1000d8 100644
--- a/drivers/usb/input/usbtouchscreen.c
+++ b/drivers/usb/input/usbtouchscreen.c
@@ -2,9 +2,12 @@
* usbtouchscreen.c
* Driver for USB Touchscreens, supporting those devices:
* - eGalax Touchkit
- * - 3M/Microtouch
+ * includes eTurboTouch CT-410/510/700
+ * - 3M/Microtouch EX II series
* - ITM
* - PanJit TouchSet
+ * - eTurboTouch
+ * - Gunze AHL61
*
* Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -42,7 +45,7 @@
#include <linux/usb/input.h>
-#define DRIVER_VERSION "v0.3"
+#define DRIVER_VERSION "v0.4"
#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
#define DRIVER_DESC "USB Touchscreen Driver"
@@ -60,6 +63,7 @@ struct usbtouch_device_info {
int flags;
void (*process_pkt) (struct usbtouch_usb *usbtouch, struct pt_regs *regs, unsigned char *pkt, int len);
+ int (*get_pkt_len) (unsigned char *pkt, int len);
int (*read_data) (unsigned char *pkt, int *x, int *y, int *touch, int *press);
int (*init) (struct usbtouch_usb *usbtouch);
};
@@ -81,8 +85,16 @@ struct usbtouch_usb {
char phys[64];
};
-static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
- struct pt_regs *regs, unsigned char *pkt, int len);
+
+#if defined(CONFIG_USB_TOUCHSCREEN_EGALAX) || defined(CONFIG_USB_TOUCHSCREEN_ETURBO)
+#define MULTI_PACKET
+#endif
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ struct pt_regs *regs,
+ unsigned char *pkt, int len);
+#endif
/* device types */
enum {
@@ -91,14 +103,19 @@ enum {
DEVTYPE_PANJIT,
DEVTYPE_3M,
DEVTYPE_ITM,
+ DEVTYPE_ETURBO,
+ DEVTYPE_GUNZE,
};
static struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
{USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
@@ -116,6 +133,14 @@ static struct usb_device_id usbtouch_devices[] = {
{USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
#endif
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+ {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+ {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
+#endif
+
{}
};
@@ -140,82 +165,23 @@ static int egalax_read_data(unsigned char *pkt, int *x, int *y, int *touch, int
*touch = pkt[0] & 0x01;
return 1;
-
}
-static int egalax_get_pkt_len(unsigned char *buf)
+static int egalax_get_pkt_len(unsigned char *buf, int len)
{
switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
case EGALAX_PKT_TYPE_REPT:
return 5;
case EGALAX_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
return buf[1] + 2;
}
return 0;
}
-
-static void egalax_process(struct usbtouch_usb *usbtouch, struct pt_regs *regs,
- unsigned char *pkt, int len)
-{
- unsigned char *buffer;
- int pkt_len, buf_len, pos;
-
- /* if the buffer contains data, append */
- if (unlikely(usbtouch->buf_len)) {
- int tmp;
-
- /* if only 1 byte in buffer, add another one to get length */
- if (usbtouch->buf_len == 1)
- usbtouch->buffer[1] = pkt[0];
-
- pkt_len = egalax_get_pkt_len(usbtouch->buffer);
-
- /* unknown packet: drop everything */
- if (!pkt_len)
- return;
-
- /* append, process */
- tmp = pkt_len - usbtouch->buf_len;
- memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
- usbtouch_process_pkt(usbtouch, regs, usbtouch->buffer, pkt_len);
-
- buffer = pkt + tmp;
- buf_len = len - tmp;
- } else {
- buffer = pkt;
- buf_len = len;
- }
-
- /* only one byte left in buffer */
- if (unlikely(buf_len == 1)) {
- usbtouch->buffer[0] = buffer[0];
- usbtouch->buf_len = 1;
- return;
- }
-
- /* loop over the buffer */
- pos = 0;
- while (pos < buf_len) {
- /* get packet len */
- pkt_len = egalax_get_pkt_len(buffer + pos);
-
- /* unknown packet: drop everything */
- if (unlikely(!pkt_len))
- return;
-
- /* full packet: process */
- if (likely(pkt_len <= buf_len)) {
- usbtouch_process_pkt(usbtouch, regs, buffer + pos, pkt_len);
- } else {
- /* incomplete packet: save in buffer */
- memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
- usbtouch->buf_len = buf_len - pos;
- }
- pos += pkt_len;
- }
-}
#endif
@@ -254,7 +220,7 @@ static int mtouch_read_data(unsigned char *pkt, int *x, int *y, int *touch, int
static int mtouch_init(struct usbtouch_usb *usbtouch)
{
- int ret;
+ int ret, i;
ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
MTOUCHUSB_RESET,
@@ -264,15 +230,20 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)
__FUNCTION__, ret);
if (ret < 0)
return ret;
-
- ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
- MTOUCHUSB_ASYNC_REPORT,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
- dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
- __FUNCTION__, ret);
- if (ret < 0)
- return ret;
+ msleep(150);
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
+ MTOUCHUSB_ASYNC_REPORT,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
+ __FUNCTION__, ret);
+ if (ret >= 0)
+ break;
+ if (ret != -EPIPE)
+ return ret;
+ }
return 0;
}
@@ -296,6 +267,54 @@ static int itm_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *pr
/*****************************************************************************
+ * eTurboTouch part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+static int eturbo_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *press)
+{
+ unsigned int shift;
+
+ /* packets should start with sync */
+ if (!(pkt[0] & 0x80))
+ return 0;
+
+ shift = (6 - (pkt[0] & 0x03));
+ *x = ((pkt[3] << 7) | pkt[4]) >> shift;
+ *y = ((pkt[1] << 7) | pkt[2]) >> shift;
+ *touch = (pkt[0] & 0x10) ? 1 : 0;
+
+ return 1;
+}
+
+static int eturbo_get_pkt_len(unsigned char *buf, int len)
+{
+ if (buf[0] & 0x80)
+ return 5;
+ if (buf[0] == 0x01)
+ return 3;
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * Gunze part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+static int gunze_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *press)
+{
+ if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
+ return 0;
+
+ *x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ *y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ *touch = pkt[0] & 0x20;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
* the different device descriptors
*/
static struct usbtouch_device_info usbtouch_dev_info[] = {
@@ -307,7 +326,8 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.max_yc = 0x07ff,
.rept_size = 16,
.flags = USBTOUCH_FLG_BUFFER,
- .process_pkt = egalax_process,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data,
},
#endif
@@ -346,6 +366,31 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.read_data = itm_read_data,
},
#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+ [DEVTYPE_ETURBO] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 8,
+ .flags = USBTOUCH_FLG_BUFFER,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = eturbo_get_pkt_len,
+ .read_data = eturbo_read_data,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+ [DEVTYPE_GUNZE] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = gunze_read_data,
+ },
+#endif
};
@@ -377,6 +422,83 @@ static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
}
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ struct pt_regs *regs,
+ unsigned char *pkt, int len)
+{
+ unsigned char *buffer;
+ int pkt_len, pos, buf_len, tmp;
+
+ /* process buffer */
+ if (unlikely(usbtouch->buf_len)) {
+ /* try to get size */
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+
+ /* drop? */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* need to append -pkt_len bytes before able to get size */
+ if (unlikely(pkt_len < 0)) {
+ int append = -pkt_len;
+ if (unlikely(append > len))
+ append = len;
+ if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
+ usbtouch->buf_len += append;
+
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+ if (pkt_len < 0)
+ return;
+ }
+
+ /* append */
+ tmp = pkt_len - usbtouch->buf_len;
+ if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
+ usbtouch_process_pkt(usbtouch, regs, usbtouch->buffer, pkt_len);
+
+ buffer = pkt + tmp;
+ buf_len = len - tmp;
+ } else {
+ buffer = pkt;
+ buf_len = len;
+ }
+
+ /* loop over the received packet, process */
+ pos = 0;
+ while (pos < buf_len) {
+ /* get packet len */
+ pkt_len = usbtouch->type->get_pkt_len(buffer + pos, len);
+
+ /* unknown packet: drop everything */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* full packet: process */
+ if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
+ usbtouch_process_pkt(usbtouch, regs, buffer + pos, pkt_len);
+ } else {
+ /* incomplete packet: save in buffer */
+ memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
+ usbtouch->buf_len = buf_len - pos;
+ return;
+ }
+ pos += pkt_len;
+ }
+
+out_flush_buf:
+ usbtouch->buf_len = 0;
+ return;
+}
+#endif
+
+
static void usbtouch_irq(struct urb *urb, struct pt_regs *regs)
{
struct usbtouch_usb *usbtouch = urb->context;
@@ -386,7 +508,7 @@ static void usbtouch_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
@@ -452,7 +574,7 @@ static int usbtouch_probe(struct usb_interface *intf,
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf);
struct usbtouch_device_info *type;
- int err;
+ int err = -ENOMEM;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
@@ -526,6 +648,7 @@ static int usbtouch_probe(struct usb_interface *intf,
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch, endpoint->bInterval);
+ usbtouch->irq->dev = usbtouch->udev;
usbtouch->irq->transfer_dma = usbtouch->data_dma;
usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -553,7 +676,7 @@ out_free_buffers:
out_free:
input_free_device(input_dev);
kfree(usbtouch);
- return -ENOMEM;
+ return err;
}
static void usbtouch_disconnect(struct usb_interface *intf)
diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c
deleted file mode 100644
index 369461a70b7..00000000000
--- a/drivers/usb/input/wacom.c
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- * USB Wacom Graphire and Wacom Intuos tablet support
- *
- * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
- * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
- * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
- * Copyright (c) 2000 James E. Blair <corvus@gnu.org>
- * Copyright (c) 2000 Daniel Egger <egger@suse.de>
- * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
- * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
- * Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com>
- *
- * ChangeLog:
- * v0.1 (vp) - Initial release
- * v0.2 (aba) - Support for all buttons / combinations
- * v0.3 (vp) - Support for Intuos added
- * v0.4 (sm) - Support for more Intuos models, menustrip
- * relative mode, proximity.
- * v0.5 (vp) - Big cleanup, nifty features removed,
- * they belong in userspace
- * v1.8 (vp) - Submit URB only when operating, moved to CVS,
- * use input_report_key instead of report_btn and
- * other cleanups
- * v1.11 (vp) - Add URB ->dev setting for new kernels
- * v1.11 (jb) - Add support for the 4D Mouse & Lens
- * v1.12 (de) - Add support for two more inking pen IDs
- * v1.14 (vp) - Use new USB device id probing scheme.
- * Fix Wacom Graphire mouse wheel
- * v1.18 (vp) - Fix mouse wheel direction
- * Make mouse relative
- * v1.20 (fl) - Report tool id for Intuos devices
- * - Multi tools support
- * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
- * - Add PL models support
- * - Fix Wacom Graphire mouse wheel again
- * v1.21 (vp) - Removed protocol descriptions
- * - Added MISC_SERIAL for tool serial numbers
- * (gb) - Identify version on module load.
- * v1.21.1 (fl) - added Graphire2 support
- * v1.21.2 (fl) - added Intuos2 support
- * - added all the PL ids
- * v1.21.3 (fl) - added another eraser id from Neil Okamoto
- * - added smooth filter for Graphire from Peri Hankey
- * - added PenPartner support from Olaf van Es
- * - new tool ids from Ole Martin Bjoerndalen
- * v1.29 (pc) - Add support for more tablets
- * - Fix pressure reporting
- * v1.30 (vp) - Merge 2.4 and 2.5 drivers
- * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
- * - Cleanups here and there
- * v1.30.1 (pi) - Added Graphire3 support
- * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
- * v1.43 (pc) - Added support for Cintiq 21UX
- * - Fixed a Graphire bug
- * - Merged wacom_intuos3_irq into wacom_intuos_irq
- * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
- * - Report Device IDs
- * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
- * - Minor data report fix
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/usb/input.h>
-#include <asm/unaligned.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.45"
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
-#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
-#define DRIVER_LICENSE "GPL"
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
-
-#define USB_VENDOR_ID_WACOM 0x056a
-#define STYLUS_DEVICE_ID 0x02
-#define CURSOR_DEVICE_ID 0x06
-#define ERASER_DEVICE_ID 0x0A
-
-enum {
- PENPARTNER = 0,
- GRAPHIRE,
- WACOM_G4,
- PL,
- INTUOS,
- INTUOS3,
- INTUOS312,
- INTUOS319,
- CINTIQ,
- MAX_TYPE
-};
-
-struct wacom_features {
- char *name;
- int pktlen;
- int x_max;
- int y_max;
- int pressure_max;
- int distance_max;
- int type;
- usb_complete_t irq;
-};
-
-struct wacom {
- signed char *data;
- dma_addr_t data_dma;
- struct input_dev *dev;
- struct usb_device *usbdev;
- struct urb *irq;
- struct wacom_features *features;
- int tool[2];
- int id[2];
- __u32 serial[2];
- char phys[32];
-};
-
-#define USB_REQ_GET_REPORT 0x01
-#define USB_REQ_SET_REPORT 0x09
-
-static int usb_get_report(struct usb_interface *intf, unsigned char type,
- unsigned char id, void *buf, int size)
-{
- return usb_control_msg(interface_to_usbdev(intf),
- usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
- USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
- buf, size, 100);
-}
-
-static int usb_set_report(struct usb_interface *intf, unsigned char type,
- unsigned char id, void *buf, int size)
-{
- return usb_control_msg(interface_to_usbdev(intf),
- usb_sndctrlpipe(interface_to_usbdev(intf), 0),
- USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
- buf, size, 1000);
-}
-
-static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int prox, pressure, id;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2) {
- dbg("wacom_pl_irq: received unknown report #%d", data[0]);
- goto exit;
- }
-
- prox = data[1] & 0x40;
-
- input_regs(dev, regs);
-
- id = ERASER_DEVICE_ID;
- if (prox) {
-
- pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
- if (wacom->features->pressure_max > 255)
- pressure = (pressure << 1) | ((data[4] >> 6) & 1);
- pressure += (wacom->features->pressure_max + 1) / 2;
-
- /*
- * if going from out of proximity into proximity select between the eraser
- * and the pen based on the state of the stylus2 button, choose eraser if
- * pressed else choose pen. if not a proximity change from out to in, send
- * an out of proximity for previous tool then a in for new tool.
- */
- if (!wacom->tool[0]) {
- /* Eraser bit set for DTF */
- if (data[1] & 0x10)
- wacom->tool[1] = BTN_TOOL_RUBBER;
- else
- /* Going into proximity select tool */
- wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
- } else {
- /* was entered with stylus2 pressed */
- if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
- /* report out proximity for previous tool */
- input_report_key(dev, wacom->tool[1], 0);
- input_sync(dev);
- wacom->tool[1] = BTN_TOOL_PEN;
- goto exit;
- }
- }
- if (wacom->tool[1] != BTN_TOOL_RUBBER) {
- /* Unknown tool selected default to pen tool */
- wacom->tool[1] = BTN_TOOL_PEN;
- id = STYLUS_DEVICE_ID;
- }
- input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */
- input_report_abs(dev, ABS_MISC, id); /* report tool id */
- input_report_abs(dev, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
- input_report_abs(dev, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
- input_report_abs(dev, ABS_PRESSURE, pressure);
-
- input_report_key(dev, BTN_TOUCH, data[4] & 0x08);
- input_report_key(dev, BTN_STYLUS, data[4] & 0x10);
- /* Only allow the stylus2 button to be reported for the pen tool. */
- input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
- } else {
- /* report proximity-out of a (valid) tool */
- if (wacom->tool[1] != BTN_TOOL_RUBBER) {
- /* Unknown tool selected default to pen tool */
- wacom->tool[1] = BTN_TOOL_PEN;
- }
- input_report_key(dev, wacom->tool[1], prox);
- }
-
- wacom->tool[0] = prox; /* Save proximity state */
- input_sync(dev);
-
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int retval, id;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2) {
- printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
- if (data[1] & 0x04) {
- input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x20);
- input_report_key(dev, BTN_TOUCH, data[1] & 0x08);
- id = ERASER_DEVICE_ID;
- } else {
- input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20);
- input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
- id = STYLUS_DEVICE_ID;
- }
- input_report_abs(dev, ABS_MISC, id); /* report tool id */
- input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2]));
- input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4]));
- input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
- input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_key(dev, BTN_STYLUS2, data[1] & 0x10);
-
- input_sync(dev);
-
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static void wacom_penpartner_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2) {
- printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
- input_report_key(dev, BTN_TOOL_PEN, 1);
- input_report_abs(dev, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
- input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1]));
- input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3]));
- input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127);
- input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
- input_report_key(dev, BTN_STYLUS, (data[5] & 0x40));
- input_sync(dev);
-
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int x, y, id, rw;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] == 99) return; /* for Volito tablets */
-
- if (data[0] != 2) {
- dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
-
- id = STYLUS_DEVICE_ID;
- if (data[1] & 0x10) { /* in prox */
-
- switch ((data[1] >> 5) & 3) {
-
- case 0: /* Pen */
- wacom->tool[0] = BTN_TOOL_PEN;
- break;
-
- case 1: /* Rubber */
- wacom->tool[0] = BTN_TOOL_RUBBER;
- id = ERASER_DEVICE_ID;
- break;
-
- case 2: /* Mouse with wheel */
- input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
- if (wacom->features->type == WACOM_G4) {
- rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
- input_report_rel(dev, REL_WHEEL, -rw);
- } else
- input_report_rel(dev, REL_WHEEL, -(signed char) data[6]);
- /* fall through */
-
- case 3: /* Mouse without wheel */
- wacom->tool[0] = BTN_TOOL_MOUSE;
- id = CURSOR_DEVICE_ID;
- input_report_key(dev, BTN_LEFT, data[1] & 0x01);
- input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
- if (wacom->features->type == WACOM_G4)
- input_report_abs(dev, ABS_DISTANCE, data[6]);
- else
- input_report_abs(dev, ABS_DISTANCE, data[7]);
- break;
- }
- }
-
- if (data[1] & 0x90) {
- x = le16_to_cpu(*(__le16 *) &data[2]);
- y = le16_to_cpu(*(__le16 *) &data[4]);
- input_report_abs(dev, ABS_X, x);
- input_report_abs(dev, ABS_Y, y);
- if (wacom->tool[0] != BTN_TOOL_MOUSE) {
- input_report_abs(dev, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
- input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
- input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
- }
- }
-
- if (data[1] & 0x10)
- input_report_abs(dev, ABS_MISC, id); /* report tool id */
- else
- input_report_abs(dev, ABS_MISC, 0); /* reset tool id */
- input_report_key(dev, wacom->tool[0], data[1] & 0x10);
- input_sync(dev);
-
- /* send pad data */
- if (wacom->features->type == WACOM_G4) {
- if ((wacom->serial[1] & 0xc0) != (data[7] & 0xf8)) {
- wacom->id[1] = 1;
- wacom->serial[1] = (data[7] & 0xf8);
- input_report_key(dev, BTN_0, (data[7] & 0x40));
- input_report_key(dev, BTN_4, (data[7] & 0x80));
- rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
- input_report_rel(dev, REL_WHEEL, rw);
- input_report_key(dev, BTN_TOOL_FINGER, 0xf0);
- input_event(dev, EV_MSC, MSC_SERIAL, 0xf0);
- } else if (wacom->id[1]) {
- wacom->id[1] = 0;
- input_report_key(dev, BTN_TOOL_FINGER, 0);
- input_event(dev, EV_MSC, MSC_SERIAL, 0xf0);
- }
- input_sync(dev);
- }
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static int wacom_intuos_inout(struct urb *urb)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int idx;
-
- /* tool number */
- idx = data[1] & 0x01;
-
- /* Enter report */
- if ((data[1] & 0xfc) == 0xc0) {
- /* serial number of the tool */
- wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
- (data[4] << 20) + (data[5] << 12) +
- (data[6] << 4) + (data[7] >> 4);
-
- wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
- switch (wacom->id[idx]) {
- case 0x812: /* Inking pen */
- case 0x801: /* Intuos3 Inking pen */
- case 0x012:
- wacom->tool[idx] = BTN_TOOL_PENCIL;
- break;
- case 0x822: /* Pen */
- case 0x842:
- case 0x852:
- case 0x823: /* Intuos3 Grip Pen */
- case 0x813: /* Intuos3 Classic Pen */
- case 0x885: /* Intuos3 Marker Pen */
- case 0x022:
- wacom->tool[idx] = BTN_TOOL_PEN;
- break;
- case 0x832: /* Stroke pen */
- case 0x032:
- wacom->tool[idx] = BTN_TOOL_BRUSH;
- break;
- case 0x007: /* Mouse 4D and 2D */
- case 0x09c:
- case 0x094:
- case 0x017: /* Intuos3 2D Mouse */
- wacom->tool[idx] = BTN_TOOL_MOUSE;
- break;
- case 0x096: /* Lens cursor */
- case 0x097: /* Intuos3 Lens cursor */
- wacom->tool[idx] = BTN_TOOL_LENS;
- break;
- case 0x82a: /* Eraser */
- case 0x85a:
- case 0x91a:
- case 0xd1a:
- case 0x0fa:
- case 0x82b: /* Intuos3 Grip Pen Eraser */
- case 0x81b: /* Intuos3 Classic Pen Eraser */
- case 0x91b: /* Intuos3 Airbrush Eraser */
- wacom->tool[idx] = BTN_TOOL_RUBBER;
- break;
- case 0xd12:
- case 0x912:
- case 0x112:
- case 0x913: /* Intuos3 Airbrush */
- wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
- break;
- default: /* Unknown tool */
- wacom->tool[idx] = BTN_TOOL_PEN;
- }
- if(!((wacom->tool[idx] == BTN_TOOL_LENS) &&
- ((wacom->features->type == INTUOS312)
- || (wacom->features->type == INTUOS319)))) {
- input_report_abs(dev, ABS_MISC, wacom->id[idx]); /* report tool id */
- input_report_key(dev, wacom->tool[idx], 1);
- input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- input_sync(dev);
- }
- return 1;
- }
-
- /* Exit report */
- if ((data[1] & 0xfe) == 0x80) {
- input_report_key(dev, wacom->tool[idx], 0);
- input_report_abs(dev, ABS_MISC, 0); /* reset tool id */
- input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- input_sync(dev);
- return 1;
- }
-
- if((wacom->tool[idx] == BTN_TOOL_LENS) && ((wacom->features->type == INTUOS312)
- || (wacom->features->type == INTUOS319)))
- return 1;
- else
- return 0;
-}
-
-static void wacom_intuos_general(struct urb *urb)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- unsigned int t;
-
- /* general pen packet */
- if ((data[1] & 0xb8) == 0xa0) {
- t = (data[6] << 2) | ((data[7] >> 6) & 3);
- input_report_abs(dev, ABS_PRESSURE, t);
- input_report_abs(dev, ABS_TILT_X,
- ((data[7] << 1) & 0x7e) | (data[8] >> 7));
- input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
- input_report_key(dev, BTN_STYLUS, data[1] & 2);
- input_report_key(dev, BTN_STYLUS2, data[1] & 4);
- input_report_key(dev, BTN_TOUCH, t > 10);
- }
-
- /* airbrush second packet */
- if ((data[1] & 0xbc) == 0xb4) {
- input_report_abs(dev, ABS_WHEEL,
- (data[6] << 2) | ((data[7] >> 6) & 3));
- input_report_abs(dev, ABS_TILT_X,
- ((data[7] << 1) & 0x7e) | (data[8] >> 7));
- input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
- }
- return;
-}
-
-static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- unsigned int t;
- int idx;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
- dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
-
- /* tool number */
- idx = data[1] & 0x01;
-
- /* pad packets. Works as a second tool and is always in prox */
- if (data[0] == 12) {
- /* initiate the pad as a device */
- if (wacom->tool[1] != BTN_TOOL_FINGER)
- wacom->tool[1] = BTN_TOOL_FINGER;
-
- input_report_key(dev, BTN_0, (data[5] & 0x01));
- input_report_key(dev, BTN_1, (data[5] & 0x02));
- input_report_key(dev, BTN_2, (data[5] & 0x04));
- input_report_key(dev, BTN_3, (data[5] & 0x08));
- input_report_key(dev, BTN_4, (data[6] & 0x01));
- input_report_key(dev, BTN_5, (data[6] & 0x02));
- input_report_key(dev, BTN_6, (data[6] & 0x04));
- input_report_key(dev, BTN_7, (data[6] & 0x08));
- input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
- input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
-
- if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | data[2])
- input_report_key(dev, wacom->tool[1], 1);
- else
- input_report_key(dev, wacom->tool[1], 0);
- input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff);
- input_sync(dev);
- goto exit;
- }
-
- /* process in/out prox events */
- if (wacom_intuos_inout(urb))
- goto exit;
-
- /* Cintiq doesn't send data when RDY bit isn't set */
- if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
- goto exit;
-
- if (wacom->features->type >= INTUOS3) {
- input_report_abs(dev, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
- input_report_abs(dev, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
- input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
- } else {
- input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2]));
- input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4]));
- input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
- }
-
- /* process general packets */
- wacom_intuos_general(urb);
-
- /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
- if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
-
- if (data[1] & 0x02) {
- /* Rotation packet */
- if (wacom->features->type >= INTUOS3) {
- /* I3 marker pen rotation reported as wheel
- * due to valuator limitation
- */
- t = (data[6] << 3) | ((data[7] >> 5) & 7);
- t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
- ((t-1) / 2 + 450)) : (450 - t / 2) ;
- input_report_abs(dev, ABS_WHEEL, t);
- } else {
- /* 4D mouse rotation packet */
- t = (data[6] << 3) | ((data[7] >> 5) & 7);
- input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ?
- ((t - 1) / 2) : -t / 2);
- }
-
- } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) {
- /* 4D mouse packet */
- input_report_key(dev, BTN_LEFT, data[8] & 0x01);
- input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
- input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
-
- input_report_key(dev, BTN_SIDE, data[8] & 0x20);
- input_report_key(dev, BTN_EXTRA, data[8] & 0x10);
- t = (data[6] << 2) | ((data[7] >> 6) & 3);
- input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
-
- } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
- /* 2D mouse packet */
- input_report_key(dev, BTN_LEFT, data[8] & 0x04);
- input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
- input_report_key(dev, BTN_RIGHT, data[8] & 0x10);
- input_report_rel(dev, REL_WHEEL, (data[8] & 0x01)
- - ((data[8] & 0x02) >> 1));
-
- /* I3 2D mouse side buttons */
- if (wacom->features->type == INTUOS3) {
- input_report_key(dev, BTN_SIDE, data[8] & 0x40);
- input_report_key(dev, BTN_EXTRA, data[8] & 0x20);
- }
-
- } else if (wacom->features->type < INTUOS3) {
- /* Lens cursor packets */
- input_report_key(dev, BTN_LEFT, data[8] & 0x01);
- input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
- input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
- input_report_key(dev, BTN_SIDE, data[8] & 0x10);
- input_report_key(dev, BTN_EXTRA, data[8] & 0x08);
- }
- }
-
- input_report_abs(dev, ABS_MISC, wacom->id[idx]); /* report tool id */
- input_report_key(dev, wacom->tool[idx], 1);
- input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- input_sync(dev);
-
-exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static struct wacom_features wacom_features[] = {
- { "Wacom Penpartner", 7, 5040, 3780, 255, 32, PENPARTNER, wacom_penpartner_irq },
- { "Wacom Graphire", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 32, WACOM_G4, wacom_graphire_irq },
- { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 32, WACOM_G4, wacom_graphire_irq },
- { "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom PenStation2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom PenPartner2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom PL400", 8, 5408, 4056, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL500", 8, 6144, 4608, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL600", 8, 6126, 4604, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL600SX", 8, 6260, 5016, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL550", 8, 6144, 4608, 511, 32, PL, wacom_pl_irq },
- { "Wacom PL800", 8, 7220, 5780, 511, 32, PL, wacom_pl_irq },
- { "Wacom PL700", 8, 6758, 5406, 511, 32, PL, wacom_pl_irq },
- { "Wacom PL510", 8, 6282, 4762, 511, 32, PL, wacom_pl_irq },
- { "Wacom DTU710", 8, 34080, 27660, 511, 32, PL, wacom_pl_irq },
- { "Wacom DTF521", 8, 6282, 4762, 511, 32, PL, wacom_pl_irq },
- { "Wacom DTF720", 8, 6858, 5506, 511, 32, PL, wacom_pl_irq },
- { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, PL, wacom_ptu_irq },
- { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 15, INTUOS312, wacom_intuos_irq },
- { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 15, INTUOS319, wacom_intuos_irq },
- { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 15, CINTIQ, wacom_intuos_irq },
- { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq },
- { }
-};
-
-static struct usb_device_id wacom_ids[] = {
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC3) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, wacom_ids);
-
-static int wacom_open(struct input_dev *dev)
-{
- struct wacom *wacom = dev->private;
-
- wacom->irq->dev = wacom->usbdev;
- if (usb_submit_urb(wacom->irq, GFP_KERNEL))
- return -EIO;
-
- return 0;
-}
-
-static void wacom_close(struct input_dev *dev)
-{
- struct wacom *wacom = dev->private;
-
- usb_kill_urb(wacom->irq);
-}
-
-static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_endpoint_descriptor *endpoint;
- struct wacom *wacom;
- struct input_dev *input_dev;
- char rep_data[2], limit = 0;
-
- wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!wacom || !input_dev)
- goto fail1;
-
- wacom->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
- if (!wacom->data)
- goto fail1;
-
- wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
- if (!wacom->irq)
- goto fail2;
-
- wacom->usbdev = dev;
- wacom->dev = input_dev;
- usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
- strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
-
- wacom->features = wacom_features + (id - wacom_ids);
- if (wacom->features->pktlen > 10)
- BUG();
-
- input_dev->name = wacom->features->name;
- usb_to_input_id(dev, &input_dev->id);
-
- input_dev->cdev.dev = &intf->dev;
- input_dev->private = wacom;
- input_dev->open = wacom_open;
- input_dev->close = wacom_close;
-
- input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
- input_set_abs_params(input_dev, ABS_X, 0, wacom->features->x_max, 4, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, wacom->features->y_max, 4, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom->features->pressure_max, 0, 0);
- input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
-
- switch (wacom->features->type) {
- case WACOM_G4:
- input_dev->evbit[0] |= BIT(EV_MSC);
- input_dev->mscbit[0] |= BIT(MSC_SERIAL);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
- /* fall through */
-
- case GRAPHIRE:
- input_dev->evbit[0] |= BIT(EV_REL);
- input_dev->relbit[0] |= BIT(REL_WHEEL);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
- input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom->features->distance_max, 0, 0);
- break;
-
- case INTUOS3:
- case INTUOS312:
- case INTUOS319:
- case CINTIQ:
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
- input_set_abs_params(input_dev, ABS_RX, 0, 4097, 0, 0);
- input_set_abs_params(input_dev, ABS_RY, 0, 4097, 0, 0);
- /* fall through */
-
- case INTUOS:
- input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
- input_dev->mscbit[0] |= BIT(MSC_SERIAL);
- input_dev->relbit[0] |= BIT(REL_WHEEL);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
- | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
- input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom->features->distance_max, 0, 0);
- input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
- input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
- input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
- input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
- input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
- break;
-
- case PL:
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
- break;
- }
-
- endpoint = &intf->cur_altsetting->endpoint[0].desc;
-
- if (wacom->features->pktlen > 10)
- BUG();
-
- usb_fill_int_urb(wacom->irq, dev,
- usb_rcvintpipe(dev, endpoint->bEndpointAddress),
- wacom->data, wacom->features->pktlen,
- wacom->features->irq, wacom, endpoint->bInterval);
- wacom->irq->transfer_dma = wacom->data_dma;
- wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- input_register_device(wacom->dev);
-
- /* Ask the tablet to report tablet data. Repeat until it succeeds */
- do {
- rep_data[0] = 2;
- rep_data[1] = 2;
- usb_set_report(intf, 3, 2, rep_data, 2);
- usb_get_report(intf, 3, 2, rep_data, 2);
- } while (rep_data[1] != 2 && limit++ < 5);
-
- usb_set_intfdata(intf, wacom);
- return 0;
-
-fail2: usb_buffer_free(dev, 10, wacom->data, wacom->data_dma);
-fail1: input_free_device(input_dev);
- kfree(wacom);
- return -ENOMEM;
-}
-
-static void wacom_disconnect(struct usb_interface *intf)
-{
- struct wacom *wacom = usb_get_intfdata (intf);
-
- usb_set_intfdata(intf, NULL);
- if (wacom) {
- usb_kill_urb(wacom->irq);
- input_unregister_device(wacom->dev);
- usb_free_urb(wacom->irq);
- usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma);
- kfree(wacom);
- }
-}
-
-static struct usb_driver wacom_driver = {
- .name = "wacom",
- .probe = wacom_probe,
- .disconnect = wacom_disconnect,
- .id_table = wacom_ids,
-};
-
-static int __init wacom_init(void)
-{
- int result = usb_register(&wacom_driver);
- if (result == 0)
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return result;
-}
-
-static void __exit wacom_exit(void)
-{
- usb_deregister(&wacom_driver);
-}
-
-module_init(wacom_init);
-module_exit(wacom_exit);
diff --git a/drivers/usb/input/wacom.h b/drivers/usb/input/wacom.h
new file mode 100644
index 00000000000..832737b658c
--- /dev/null
+++ b/drivers/usb/input/wacom.h
@@ -0,0 +1,132 @@
+/*
+ * drivers/usb/input/wacom.h
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support
+ *
+ * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
+ * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
+ * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
+ * Copyright (c) 2000 James E. Blair <corvus@gnu.org>
+ * Copyright (c) 2000 Daniel Egger <egger@suse.de>
+ * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
+ * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
+ * Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com>
+ *
+ * ChangeLog:
+ * v0.1 (vp) - Initial release
+ * v0.2 (aba) - Support for all buttons / combinations
+ * v0.3 (vp) - Support for Intuos added
+ * v0.4 (sm) - Support for more Intuos models, menustrip
+ * relative mode, proximity.
+ * v0.5 (vp) - Big cleanup, nifty features removed,
+ * they belong in userspace
+ * v1.8 (vp) - Submit URB only when operating, moved to CVS,
+ * use input_report_key instead of report_btn and
+ * other cleanups
+ * v1.11 (vp) - Add URB ->dev setting for new kernels
+ * v1.11 (jb) - Add support for the 4D Mouse & Lens
+ * v1.12 (de) - Add support for two more inking pen IDs
+ * v1.14 (vp) - Use new USB device id probing scheme.
+ * Fix Wacom Graphire mouse wheel
+ * v1.18 (vp) - Fix mouse wheel direction
+ * Make mouse relative
+ * v1.20 (fl) - Report tool id for Intuos devices
+ * - Multi tools support
+ * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
+ * - Add PL models support
+ * - Fix Wacom Graphire mouse wheel again
+ * v1.21 (vp) - Removed protocol descriptions
+ * - Added MISC_SERIAL for tool serial numbers
+ * (gb) - Identify version on module load.
+ * v1.21.1 (fl) - added Graphire2 support
+ * v1.21.2 (fl) - added Intuos2 support
+ * - added all the PL ids
+ * v1.21.3 (fl) - added another eraser id from Neil Okamoto
+ * - added smooth filter for Graphire from Peri Hankey
+ * - added PenPartner support from Olaf van Es
+ * - new tool ids from Ole Martin Bjoerndalen
+ * v1.29 (pc) - Add support for more tablets
+ * - Fix pressure reporting
+ * v1.30 (vp) - Merge 2.4 and 2.5 drivers
+ * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
+ * - Cleanups here and there
+ * v1.30.1 (pi) - Added Graphire3 support
+ * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
+ * v1.43 (pc) - Added support for Cintiq 21UX
+ * - Fixed a Graphire bug
+ * - Merged wacom_intuos3_irq into wacom_intuos_irq
+ * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
+ * - Report Device IDs
+ * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
+ * - Minor data report fix
+ * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
+ * - where wacom_sys.c deals with system specific code,
+ * - and wacom_wac.c deals with Wacom specific code
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef WACOM_H
+#define WACOM_H
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.46"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_WACOM 0x056a
+
+struct wacom {
+ dma_addr_t data_dma;
+ struct input_dev *dev;
+ struct usb_device *usbdev;
+ struct urb *irq;
+ struct wacom_wac * wacom_wac;
+ char phys[32];
+};
+
+struct wacom_combo {
+ struct wacom * wacom;
+ struct urb * urb;
+ struct pt_regs *regs;
+};
+
+extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo);
+extern void wacom_sys_irq(struct urb *urb, struct pt_regs *regs);
+extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data);
+extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data);
+extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data);
+extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value);
+extern void wacom_input_regs(void *wcombo);
+extern void wacom_input_sync(void *wcombo);
+extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern __u16 wacom_le16_to_cpu(unsigned char *data);
+extern __u16 wacom_be16_to_cpu(unsigned char *data);
+extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id);
+extern const struct usb_device_id * get_device_table(void);
+
+#endif
diff --git a/drivers/usb/input/wacom_sys.c b/drivers/usb/input/wacom_sys.c
new file mode 100644
index 00000000000..7c3b52bdd9d
--- /dev/null
+++ b/drivers/usb/input/wacom_sys.c
@@ -0,0 +1,315 @@
+/*
+ * drivers/usb/input/wacom_sys.c
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support - system specific code
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "wacom.h"
+#include "wacom_wac.h"
+
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_SET_REPORT 0x09
+
+static int usb_get_report(struct usb_interface *intf, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(interface_to_usbdev(intf),
+ usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+ USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 100);
+}
+
+static int usb_set_report(struct usb_interface *intf, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(interface_to_usbdev(intf),
+ usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+ USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 1000);
+}
+
+static struct input_dev * get_input_dev(struct wacom_combo *wcombo)
+{
+ return wcombo->wacom->dev;
+}
+
+void wacom_sys_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct wacom *wacom = urb->context;
+ struct wacom_combo wcombo;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ wcombo.wacom = wacom;
+ wcombo.urb = urb;
+ wcombo.regs = regs;
+
+ if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo))
+ input_sync(get_input_dev(&wcombo));
+
+ exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+}
+
+void wacom_report_key(void *wcombo, unsigned int key_type, int key_data)
+{
+ input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data);
+ return;
+}
+
+void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data)
+{
+ input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data);
+ return;
+}
+
+void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data)
+{
+ input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data);
+ return;
+}
+
+void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value)
+{
+ input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value);
+ return;
+}
+
+__u16 wacom_be16_to_cpu(unsigned char *data)
+{
+ __u16 value;
+ value = be16_to_cpu(*(__be16 *) data);
+ return value;
+}
+
+__u16 wacom_le16_to_cpu(unsigned char *data)
+{
+ __u16 value;
+ value = be16_to_cpu(*(__be16 *) data);
+ return value;
+}
+
+void wacom_input_regs(void *wcombo)
+{
+ input_regs(get_input_dev((struct wacom_combo *)wcombo), ((struct wacom_combo *)wcombo)->regs);
+ return;
+}
+
+void wacom_input_sync(void *wcombo)
+{
+ input_sync(get_input_dev((struct wacom_combo *)wcombo));
+ return;
+}
+
+static int wacom_open(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ wacom->irq->dev = wacom->usbdev;
+ if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ usb_kill_urb(wacom->irq);
+}
+
+void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_MSC);
+ input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+}
+
+void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_REL);
+ input_dev->relbit[0] |= BIT(REL_WHEEL);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+}
+
+void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+ input_set_abs_params(input_dev, ABS_RX, 0, 4097, 0, 0);
+ input_set_abs_params(input_dev, ABS_RY, 0, 4097, 0, 0);
+}
+
+void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
+ input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+ input_dev->relbit[0] |= BIT(REL_WHEEL);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
+ | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
+ input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
+}
+
+void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
+}
+
+void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER);
+}
+
+static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct wacom *wacom;
+ struct wacom_wac *wacom_wac;
+ struct input_dev *input_dev;
+ char rep_data[2], limit = 0;
+
+ wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+ wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!wacom || !input_dev || !wacom_wac)
+ goto fail1;
+
+ wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
+ if (!wacom_wac->data)
+ goto fail1;
+
+ wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!wacom->irq)
+ goto fail2;
+
+ wacom->usbdev = dev;
+ wacom->dev = input_dev;
+ usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
+ strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
+
+ wacom_wac->features = get_wacom_feature(id);
+ if (wacom_wac->features->pktlen > 10)
+ BUG();
+
+ input_dev->name = wacom_wac->features->name;
+ wacom->wacom_wac = wacom_wac;
+ usb_to_input_id(dev, &input_dev->id);
+
+ input_dev->cdev.dev = &intf->dev;
+ input_dev->private = wacom;
+ input_dev->open = wacom_open;
+ input_dev->close = wacom_close;
+
+ input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
+ input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0);
+ input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
+
+ wacom_init_input_dev(input_dev, wacom_wac);
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(wacom->irq, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ wacom_wac->data, wacom_wac->features->pktlen,
+ wacom_wac->features->irq, wacom, endpoint->bInterval);
+ wacom->irq->transfer_dma = wacom->data_dma;
+ wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ input_register_device(wacom->dev);
+
+ /* Ask the tablet to report tablet data. Repeat until it succeeds */
+ do {
+ rep_data[0] = 2;
+ rep_data[1] = 2;
+ usb_set_report(intf, 3, 2, rep_data, 2);
+ usb_get_report(intf, 3, 2, rep_data, 2);
+ } while (rep_data[1] != 2 && limit++ < 5);
+
+ usb_set_intfdata(intf, wacom);
+ return 0;
+
+fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
+fail1: input_free_device(input_dev);
+ kfree(wacom);
+ kfree(wacom_wac);
+ return -ENOMEM;
+}
+
+static void wacom_disconnect(struct usb_interface *intf)
+{
+ struct wacom *wacom = usb_get_intfdata (intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (wacom) {
+ usb_kill_urb(wacom->irq);
+ input_unregister_device(wacom->dev);
+ usb_free_urb(wacom->irq);
+ usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
+ kfree(wacom);
+ kfree(wacom->wacom_wac);
+ }
+}
+
+static struct usb_driver wacom_driver = {
+ .name = "wacom",
+ .probe = wacom_probe,
+ .disconnect = wacom_disconnect,
+};
+
+static int __init wacom_init(void)
+{
+ int result;
+ wacom_driver.id_table = get_device_table();
+ result = usb_register(&wacom_driver);
+ if (result == 0)
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return result;
+}
+
+static void __exit wacom_exit(void)
+{
+ usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
diff --git a/drivers/usb/input/wacom_wac.c b/drivers/usb/input/wacom_wac.c
new file mode 100644
index 00000000000..85d458c98b6
--- /dev/null
+++ b/drivers/usb/input/wacom_wac.c
@@ -0,0 +1,646 @@
+/*
+ * drivers/usb/input/wacom_wac.c
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include "wacom.h"
+#include "wacom_wac.h"
+
+static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+
+ switch (data[0]) {
+ case 1:
+ wacom_input_regs(wcombo);
+ if (data[5] & 0x80) {
+ wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
+ wacom_report_key(wcombo, wacom->tool[0], 1);
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+ wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127));
+ wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+ } else {
+ wacom_report_key(wcombo, wacom->tool[0], 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */
+ wacom_report_abs(wcombo, ABS_PRESSURE, -1);
+ wacom_report_key(wcombo, BTN_TOUCH, 0);
+ }
+ break;
+ case 2:
+ wacom_input_regs(wcombo);
+ wacom_report_key(wcombo, BTN_TOOL_PEN, 1);
+ wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+ wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
+ wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+ break;
+ default:
+ printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
+ return 0;
+ }
+ return 1;
+}
+
+static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int prox, id, pressure;
+
+ if (data[0] != 2) {
+ dbg("wacom_pl_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ prox = data[1] & 0x40;
+
+ wacom_input_regs(wcombo);
+
+ id = ERASER_DEVICE_ID;
+ if (prox) {
+
+ pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+ if (wacom->features->pressure_max > 255)
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ pressure += (wacom->features->pressure_max + 1) / 2;
+
+ /*
+ * if going from out of proximity into proximity select between the eraser
+ * and the pen based on the state of the stylus2 button, choose eraser if
+ * pressed else choose pen. if not a proximity change from out to in, send
+ * an out of proximity for previous tool then a in for new tool.
+ */
+ if (!wacom->tool[0]) {
+ /* Eraser bit set for DTF */
+ if (data[1] & 0x10)
+ wacom->tool[1] = BTN_TOOL_RUBBER;
+ else
+ /* Going into proximity select tool */
+ wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ } else {
+ /* was entered with stylus2 pressed */
+ if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
+ /* report out proximity for previous tool */
+ wacom_report_key(wcombo, wacom->tool[1], 0);
+ wacom_input_sync(wcombo);
+ wacom->tool[1] = BTN_TOOL_PEN;
+ return 0;
+ }
+ }
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ id = STYLUS_DEVICE_ID;
+ }
+ wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+ wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+ wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
+
+ wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08);
+ wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ } else {
+ /* report proximity-out of a (valid) tool */
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ }
+ wacom_report_key(wcombo, wacom->tool[1], prox);
+ }
+
+ wacom->tool[0] = prox; /* Save proximity state */
+ return 1;
+}
+
+static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int id;
+
+ if (data[0] != 2) {
+ printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
+ return 0;
+ }
+
+ wacom_input_regs(wcombo);
+ if (data[1] & 0x04) {
+ wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20);
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08);
+ id = ERASER_DEVICE_ID;
+ } else {
+ wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20);
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+ id = STYLUS_DEVICE_ID;
+ }
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
+ return 1;
+}
+
+static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int x, y, id, rw;
+
+ if (data[0] != 2) {
+ dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ wacom_input_regs(wcombo);
+
+ id = STYLUS_DEVICE_ID;
+ if (data[1] & 0x10) { /* in prox */
+
+ switch ((data[1] >> 5) & 3) {
+
+ case 0: /* Pen */
+ wacom->tool[0] = BTN_TOOL_PEN;
+ break;
+
+ case 1: /* Rubber */
+ wacom->tool[0] = BTN_TOOL_RUBBER;
+ id = ERASER_DEVICE_ID;
+ break;
+
+ case 2: /* Mouse with wheel */
+ wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04);
+ if (wacom->features->type == WACOM_G4) {
+ rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
+ wacom_report_rel(wcombo, REL_WHEEL, -rw);
+ } else
+ wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]);
+ /* fall through */
+
+ case 3: /* Mouse without wheel */
+ wacom->tool[0] = BTN_TOOL_MOUSE;
+ id = CURSOR_DEVICE_ID;
+ wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
+ wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
+ if (wacom->features->type == WACOM_G4)
+ wacom_report_abs(wcombo, ABS_DISTANCE, data[6]);
+ else
+ wacom_report_abs(wcombo, ABS_DISTANCE, data[7]);
+ break;
+ }
+ }
+
+ if (data[1] & 0x90) {
+ x = wacom_le16_to_cpu(&data[2]);
+ y = wacom_le16_to_cpu(&data[4]);
+ wacom_report_abs(wcombo, ABS_X, x);
+ wacom_report_abs(wcombo, ABS_Y, y);
+ if (wacom->tool[0] != BTN_TOOL_MOUSE) {
+ wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
+ }
+ }
+
+ if (data[1] & 0x10)
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ else
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+ wacom_report_key(wcombo, wacom->tool[0], data[1] & 0x10);
+ wacom_input_sync(wcombo);
+
+ /* send pad data */
+ if (wacom->features->type == WACOM_G4) {
+ if ( (wacom->serial[1] & 0xc0) != (data[7] & 0xf8) ) {
+ wacom->id[1] = 1;
+ wacom->serial[1] = (data[7] & 0xf8);
+ wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
+ wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
+ rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
+ wacom_report_rel(wcombo, REL_WHEEL, rw);
+ wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+ } else if (wacom->id[1]) {
+ wacom->id[1] = 0;
+ wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+ }
+ }
+ return 1;
+}
+
+static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int idx;
+
+ /* tool number */
+ idx = data[1] & 0x01;
+
+ /* Enter report */
+ if ((data[1] & 0xfc) == 0xc0) {
+ /* serial number of the tool */
+ wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
+ (data[4] << 20) + (data[5] << 12) +
+ (data[6] << 4) + (data[7] >> 4);
+
+ wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
+ switch (wacom->id[idx]) {
+ case 0x812: /* Inking pen */
+ case 0x801: /* Intuos3 Inking pen */
+ case 0x012:
+ wacom->tool[idx] = BTN_TOOL_PENCIL;
+ break;
+ case 0x822: /* Pen */
+ case 0x842:
+ case 0x852:
+ case 0x823: /* Intuos3 Grip Pen */
+ case 0x813: /* Intuos3 Classic Pen */
+ case 0x885: /* Intuos3 Marker Pen */
+ case 0x022:
+ wacom->tool[idx] = BTN_TOOL_PEN;
+ break;
+ case 0x832: /* Stroke pen */
+ case 0x032:
+ wacom->tool[idx] = BTN_TOOL_BRUSH;
+ break;
+ case 0x007: /* Mouse 4D and 2D */
+ case 0x09c:
+ case 0x094:
+ case 0x017: /* Intuos3 2D Mouse */
+ wacom->tool[idx] = BTN_TOOL_MOUSE;
+ break;
+ case 0x096: /* Lens cursor */
+ case 0x097: /* Intuos3 Lens cursor */
+ wacom->tool[idx] = BTN_TOOL_LENS;
+ break;
+ case 0x82a: /* Eraser */
+ case 0x85a:
+ case 0x91a:
+ case 0xd1a:
+ case 0x0fa:
+ case 0x82b: /* Intuos3 Grip Pen Eraser */
+ case 0x81b: /* Intuos3 Classic Pen Eraser */
+ case 0x91b: /* Intuos3 Airbrush Eraser */
+ wacom->tool[idx] = BTN_TOOL_RUBBER;
+ break;
+ case 0xd12:
+ case 0x912:
+ case 0x112:
+ case 0x913: /* Intuos3 Airbrush */
+ wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
+ break;
+ default: /* Unknown tool */
+ wacom->tool[idx] = BTN_TOOL_PEN;
+ }
+ /* only large I3 support Lens Cursor */
+ if(!((wacom->tool[idx] == BTN_TOOL_LENS) &&
+ (wacom->features->type == INTUOS3))) {
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
+ wacom_report_key(wcombo, wacom->tool[idx], 1);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 2;
+ }
+ return 1;
+ }
+
+ /* Exit report */
+ if ((data[1] & 0xfe) == 0x80) {
+ wacom_report_key(wcombo, wacom->tool[idx], 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 2;
+ }
+ return 0;
+}
+
+static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ unsigned int t;
+
+ /* general pen packet */
+ if ((data[1] & 0xb8) == 0xa0) {
+ t = (data[6] << 2) | ((data[7] >> 6) & 3);
+ wacom_report_abs(wcombo, ABS_PRESSURE, t);
+ wacom_report_abs(wcombo, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4);
+ wacom_report_key(wcombo, BTN_TOUCH, t > 10);
+ }
+
+ /* airbrush second packet */
+ if ((data[1] & 0xbc) == 0xb4) {
+ wacom_report_abs(wcombo, ABS_WHEEL,
+ (data[6] << 2) | ((data[7] >> 6) & 3));
+ wacom_report_abs(wcombo, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+ }
+ return;
+}
+
+static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ unsigned int t;
+ int idx, result;
+
+ if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
+ dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ wacom_input_regs(wcombo);
+
+ /* tool number */
+ idx = data[1] & 0x01;
+
+ /* pad packets. Works as a second tool and is always in prox */
+ if (data[0] == 12) {
+ /* initiate the pad as a device */
+ if (wacom->tool[1] != BTN_TOOL_FINGER)
+ wacom->tool[1] = BTN_TOOL_FINGER;
+
+ wacom_report_key(wcombo, BTN_0, (data[5] & 0x01));
+ wacom_report_key(wcombo, BTN_1, (data[5] & 0x02));
+ wacom_report_key(wcombo, BTN_2, (data[5] & 0x04));
+ wacom_report_key(wcombo, BTN_3, (data[5] & 0x08));
+ wacom_report_key(wcombo, BTN_4, (data[6] & 0x01));
+ wacom_report_key(wcombo, BTN_5, (data[6] & 0x02));
+ wacom_report_key(wcombo, BTN_6, (data[6] & 0x04));
+ wacom_report_key(wcombo, BTN_7, (data[6] & 0x08));
+ wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
+ wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
+
+ if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | data[2])
+ wacom_report_key(wcombo, wacom->tool[1], 1);
+ else
+ wacom_report_key(wcombo, wacom->tool[1], 0);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
+ return 1;
+ }
+
+ /* process in/out prox events */
+ result = wacom_intuos_inout(wacom, wcombo);
+ if (result)
+ return result-1;
+
+ /* Cintiq doesn't send data when RDY bit isn't set */
+ if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
+ return 0;
+
+ if (wacom->features->type >= INTUOS3) {
+ wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
+ wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
+ wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
+ } else {
+ wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4]));
+ wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
+ }
+
+ /* process general packets */
+ wacom_intuos_general(wacom, wcombo);
+
+ /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
+ if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
+
+ if (data[1] & 0x02) {
+ /* Rotation packet */
+ if (wacom->features->type >= INTUOS3) {
+ /* I3 marker pen rotation reported as wheel
+ * due to valuator limitation
+ */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+ ((t-1) / 2 + 450)) : (450 - t / 2) ;
+ wacom_report_abs(wcombo, ABS_WHEEL, t);
+ } else {
+ /* 4D mouse rotation packet */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ?
+ ((t - 1) / 2) : -t / 2);
+ }
+
+ } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) {
+ /* 4D mouse packet */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
+
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10);
+ t = (data[6] << 2) | ((data[7] >> 6) & 3);
+ wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
+
+ } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
+ /* 2D mouse packet */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10);
+ wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01)
+ - ((data[8] & 0x02) >> 1));
+
+ /* I3 2D mouse side buttons */
+ if (wacom->features->type == INTUOS3) {
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20);
+ }
+
+ } else if (wacom->features->type < INTUOS3) {
+ /* Lens cursor packets */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08);
+ }
+ }
+
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
+ wacom_report_key(wcombo, wacom->tool[idx], 1);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 1;
+}
+
+int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
+{
+ switch (wacom_wac->features->type) {
+ case PENPARTNER:
+ return (wacom_penpartner_irq(wacom_wac, wcombo));
+ break;
+ case PL:
+ return (wacom_pl_irq(wacom_wac, wcombo));
+ break;
+ case WACOM_G4:
+ case GRAPHIRE:
+ return (wacom_graphire_irq(wacom_wac, wcombo));
+ break;
+ case PTU:
+ return (wacom_ptu_irq(wacom_wac, wcombo));
+ break;
+ case INTUOS:
+ case INTUOS3:
+ case INTUOS3L:
+ case CINTIQ:
+ return (wacom_intuos_irq(wacom_wac, wcombo));
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ switch (wacom_wac->features->type) {
+ case WACOM_G4:
+ input_dev_g4(input_dev, wacom_wac);
+ /* fall through */
+ case GRAPHIRE:
+ input_dev_g(input_dev, wacom_wac);
+ break;
+ case INTUOS3:
+ case INTUOS3L:
+ case CINTIQ:
+ input_dev_i3(input_dev, wacom_wac);
+ /* fall through */
+ case INTUOS:
+ input_dev_i(input_dev, wacom_wac);
+ break;
+ case PL:
+ case PTU:
+ input_dev_pl(input_dev, wacom_wac);
+ break;
+ case PENPARTNER:
+ input_dev_pt(input_dev, wacom_wac);
+ break;
+ }
+ return;
+}
+
+static struct wacom_features wacom_features[] = {
+ { "Wacom Penpartner", 7, 5040, 3780, 255, 32, PENPARTNER, wacom_sys_irq },
+ { "Wacom Graphire", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 32, WACOM_G4, wacom_sys_irq },
+ { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 32, WACOM_G4, wacom_sys_irq },
+ { "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom PenStation2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom PenPartner2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_sys_irq},
+ { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_sys_irq},
+ { "Wacom PL400", 8, 5408, 4056, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL500", 8, 6144, 4608, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL600", 8, 6126, 4604, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL600SX", 8, 6260, 5016, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL550", 8, 6144, 4608, 511, 32, PL, wacom_sys_irq },
+ { "Wacom PL800", 8, 7220, 5780, 511, 32, PL, wacom_sys_irq },
+ { "Wacom PL700", 8, 6758, 5406, 511, 32, PL, wacom_sys_irq },
+ { "Wacom PL510", 8, 6282, 4762, 511, 32, PL, wacom_sys_irq },
+ { "Wacom DTU710", 8, 34080, 27660, 511, 32, PL, wacom_sys_irq },
+ { "Wacom DTF521", 8, 6282, 4762, 511, 32, PL, wacom_sys_irq },
+ { "Wacom DTF720", 8, 6858, 5506, 511, 32, PL, wacom_sys_irq },
+ { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, PTU, wacom_sys_irq },
+ { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 15, INTUOS3L, wacom_sys_irq },
+ { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 15, INTUOS3L, wacom_sys_irq },
+ { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 15, CINTIQ, wacom_sys_irq },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
+ { }
+};
+
+static struct usb_device_id wacom_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
+ { }
+};
+
+const struct usb_device_id * get_device_table(void) {
+ const struct usb_device_id * id_table = wacom_ids;
+ return id_table;
+}
+
+struct wacom_features * get_wacom_feature(const struct usb_device_id * id) {
+ int index = id - wacom_ids;
+ struct wacom_features *wf = &wacom_features[index];
+ return wf;
+}
+
+MODULE_DEVICE_TABLE(usb, wacom_ids);
diff --git a/drivers/usb/input/wacom_wac.h b/drivers/usb/input/wacom_wac.h
new file mode 100644
index 00000000000..ceae7bf59d9
--- /dev/null
+++ b/drivers/usb/input/wacom_wac.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/usb/input/wacom_wac.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef WACOM_WAC_H
+#define WACOM_WAC_H
+
+#define STYLUS_DEVICE_ID 0x02
+#define CURSOR_DEVICE_ID 0x06
+#define ERASER_DEVICE_ID 0x0A
+
+enum {
+ PENPARTNER = 0,
+ GRAPHIRE,
+ WACOM_G4,
+ PTU,
+ PL,
+ INTUOS,
+ INTUOS3,
+ INTUOS3L,
+ CINTIQ,
+ MAX_TYPE
+};
+
+struct wacom_features {
+ char *name;
+ int pktlen;
+ int x_max;
+ int y_max;
+ int pressure_max;
+ int distance_max;
+ int type;
+ usb_complete_t irq;
+};
+
+struct wacom_wac {
+ signed char *data;
+ int tool[2];
+ int id[2];
+ __u32 serial[2];
+ struct wacom_features *features;
+};
+
+#endif
diff --git a/drivers/usb/input/yealink.c b/drivers/usb/input/yealink.c
index 7b45fd3de91..7291e7a2717 100644
--- a/drivers/usb/input/yealink.c
+++ b/drivers/usb/input/yealink.c
@@ -971,7 +971,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
DRIVER_VERSION, sizeof(DRIVER_VERSION));
/* Register sysfs hooks (don't care about failure) */
- sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
+ ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
return 0;
}
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 88928a4be80..c29658f69e2 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -32,6 +32,16 @@ config USB_EMI26
To compile this driver as a module, choose M here: the
module will be called emi26.
+config USB_ADUTUX
+ tristate "ADU devices from Ontrak Control Systems (EXPERIMENTAL)"
+ depends on USB && EXPERIMENTAL
+ help
+ Say Y if you want to use an ADU device from Ontrak Control
+ Systems.
+
+ To compile this driver as a module, choose M here. The module
+ will be called adutux.
+
config USB_AUERSWALD
tristate "USB Auerswald ISDN support (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
@@ -115,19 +125,36 @@ config USB_CYTHERM
To compile this driver as a module, choose M here: the
module will be called cytherm.
-config USB_PHIDGETKIT
- tristate "USB PhidgetKit support"
+config USB_PHIDGET
+ tristate "USB Phidgets drivers"
depends on USB
help
- Say Y here if you want to connect a PhidgetKit USB device from
- Phidgets Inc.
+ Say Y here to enable the various drivers for devices from
+ Phidgets inc.
+
+config USB_PHIDGETKIT
+ tristate "USB PhidgetInterfaceKit support"
+ depends on USB_PHIDGET
+ help
+ Say Y here if you want to connect a PhidgetInterfaceKit USB device
+ from Phidgets Inc.
To compile this driver as a module, choose M here: the
module will be called phidgetkit.
+config USB_PHIDGETMOTORCONTROL
+ tristate "USB PhidgetMotorControl support"
+ depends on USB_PHIDGET
+ help
+ Say Y here if you want to connect a PhidgetMotorControl USB device
+ from Phidgets Inc.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phidgetmotorcontrol.
+
config USB_PHIDGETSERVO
tristate "USB PhidgetServo support"
- depends on USB
+ depends on USB_PHIDGET
help
Say Y here if you want to connect an 1 or 4 Motor PhidgetServo
servo controller version 2.0 or 3.0.
@@ -151,6 +178,30 @@ config USB_IDMOUSE
See also <http://www.fs.tum.de/~echtler/idmouse/>.
+config USB_FTDI_ELAN
+ tristate "Elan PCMCIA CardBus Adapter USB Client"
+ depends on USB
+ default M
+ help
+ ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters.
+ Currently only the U132 adapter is available.
+
+ The U132 is specifically designed for CardBus PC cards that contain
+ an OHCI host controller. Typical PC cards are the Orange Mobile 3G
+ Option GlobeTrotter Fusion card. The U132 adapter will *NOT* work
+ with PC cards that do not contain an OHCI controller. To use a U132
+ adapter you will need this "ftdi-elan" module as well as the "u132-hcd"
+ module which is a USB host controller driver that talks to the OHCI
+ controller within CardBus card that are inserted in the U132 adapter.
+
+ This driver has been tested with a CardBus OHCI USB adapter, and
+ worked with a USB PEN Drive inserted into the first USB port of
+ the PCCARD. A rather pointless thing to do, but useful for testing.
+
+ See also the USB_U132_HCD entry "Elan U132 Adapter Host Controller"
+
+ It is safe to say M here.
+
config USB_APPLEDISPLAY
tristate "Apple Cinema Display support"
depends on USB
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 2927260c581..2be70fa259b 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -3,22 +3,25 @@
# (the ones that don't fit into any other categories)
#
+obj-$(CONFIG_USB_ADUTUX) += adutux.o
obj-$(CONFIG_USB_AUERSWALD) += auerswald.o
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o
obj-$(CONFIG_USB_CYTHERM) += cytherm.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o
+obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
+obj-$(CONFIG_USB_PHIDGET) += phidget.o
obj-$(CONFIG_USB_PHIDGETKIT) += phidgetkit.o
+obj-$(CONFIG_USB_PHIDGETMOTORCONTROL) += phidgetmotorcontrol.o
obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_USS720) += uss720.o
-obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
new file mode 100644
index 00000000000..d3963199b6e
--- /dev/null
+++ b/drivers/usb/misc/adutux.c
@@ -0,0 +1,900 @@
+/*
+ * adutux - driver for ADU devices from Ontrak Control Systems
+ * This is an experimental driver. Use at your own risk.
+ * This driver is not supported by Ontrak Control Systems.
+ *
+ * Copyright (c) 2003 John Homppi (SCO, leave this notice here)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * derived from the Lego USB Tower driver 0.56:
+ * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net>
+ * 2001 Juergen Stuber <stuber@loria.fr>
+ * that was derived from USB Skeleton driver - 0.5
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 5;
+#else
+static int debug = 1;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(lvl, format, arg...) \
+do { \
+ if (debug >= lvl) \
+ printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); \
+} while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.0.13"
+#define DRIVER_AUTHOR "John Homppi"
+#define DRIVER_DESC "adutux (see www.ontrak.net)"
+
+/* Module parameters */
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+/* Define these values to match your device */
+#define ADU_VENDOR_ID 0x0a07
+#define ADU_PRODUCT_ID 0x0064
+
+/* table of devices that work with this driver */
+static struct usb_device_id device_table [] = {
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) }, /* ADU100 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) }, /* ADU120 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) }, /* ADU130 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) }, /* ADU200 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) }, /* ADU208 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) }, /* ADU218 */
+ { }/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define ADU_MINOR_BASE 0
+#else
+#define ADU_MINOR_BASE 67
+#endif
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES 16
+
+#define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */
+
+/* Structure to hold all of our device specific stuff */
+struct adu_device {
+ struct semaphore sem; /* locks this structure */
+ struct usb_device* udev; /* save off the usb device pointer */
+ struct usb_interface* interface;
+ unsigned char minor; /* the starting minor number for this device */
+ char serial_number[8];
+
+ int open_count; /* number of times this port has been opened */
+
+ char* read_buffer_primary;
+ int read_buffer_length;
+ char* read_buffer_secondary;
+ int secondary_head;
+ int secondary_tail;
+ spinlock_t buflock;
+
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ char* interrupt_in_buffer;
+ struct usb_endpoint_descriptor* interrupt_in_endpoint;
+ struct urb* interrupt_in_urb;
+ int read_urb_finished;
+
+ char* interrupt_out_buffer;
+ struct usb_endpoint_descriptor* interrupt_out_endpoint;
+ struct urb* interrupt_out_urb;
+};
+
+/* prevent races between open() and disconnect */
+static DEFINE_MUTEX(disconnect_mutex);
+static struct usb_driver adu_driver;
+
+static void adu_debug_data(int level, const char *function, int size,
+ const unsigned char *data)
+{
+ int i;
+
+ if (debug < level)
+ return;
+
+ printk(KERN_DEBUG __FILE__": %s - length = %d, data = ",
+ function, size);
+ for (i = 0; i < size; ++i)
+ printk("%.2x ", data[i]);
+ printk("\n");
+}
+
+/**
+ * adu_abort_transfers
+ * aborts transfers and frees associated data structures
+ */
+static void adu_abort_transfers(struct adu_device *dev)
+{
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (dev == NULL) {
+ dbg(1," %s : dev is null", __FUNCTION__);
+ goto exit;
+ }
+
+ if (dev->udev == NULL) {
+ dbg(1," %s : udev is null", __FUNCTION__);
+ goto exit;
+ }
+
+ dbg(2," %s : udev state %d", __FUNCTION__, dev->udev->state);
+ if (dev->udev->state == USB_STATE_NOTATTACHED) {
+ dbg(1," %s : udev is not attached", __FUNCTION__);
+ goto exit;
+ }
+
+ /* shutdown transfer */
+ usb_unlink_urb(dev->interrupt_in_urb);
+ usb_unlink_urb(dev->interrupt_out_urb);
+
+exit:
+ dbg(2," %s : leave", __FUNCTION__);
+}
+
+static void adu_delete(struct adu_device *dev)
+{
+ dbg(2, "%s enter", __FUNCTION__);
+
+ adu_abort_transfers(dev);
+
+ /* free data structures */
+ usb_free_urb(dev->interrupt_in_urb);
+ usb_free_urb(dev->interrupt_out_urb);
+ kfree(dev->read_buffer_primary);
+ kfree(dev->read_buffer_secondary);
+ kfree(dev->interrupt_in_buffer);
+ kfree(dev->interrupt_out_buffer);
+ kfree(dev);
+
+ dbg(2, "%s : leave", __FUNCTION__);
+}
+
+static void adu_interrupt_in_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct adu_device *dev = urb->context;
+
+ dbg(4," %s : enter, status %d", __FUNCTION__, urb->status);
+ adu_debug_data(5, __FUNCTION__, urb->actual_length,
+ urb->transfer_buffer);
+
+ spin_lock(&dev->buflock);
+
+ if (urb->status != 0) {
+ if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) {
+ dbg(1," %s : nonzero status received: %d",
+ __FUNCTION__, urb->status);
+ }
+ goto exit;
+ }
+
+ if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) {
+ if (dev->read_buffer_length <
+ (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) -
+ (urb->actual_length)) {
+ memcpy (dev->read_buffer_primary +
+ dev->read_buffer_length,
+ dev->interrupt_in_buffer, urb->actual_length);
+
+ dev->read_buffer_length += urb->actual_length;
+ dbg(2," %s reading %d ", __FUNCTION__,
+ urb->actual_length);
+ } else {
+ dbg(1," %s : read_buffer overflow", __FUNCTION__);
+ }
+ }
+
+exit:
+ dev->read_urb_finished = 1;
+ spin_unlock(&dev->buflock);
+ /* always wake up so we recover from errors */
+ wake_up_interruptible(&dev->read_wait);
+ adu_debug_data(5, __FUNCTION__, urb->actual_length,
+ urb->transfer_buffer);
+ dbg(4," %s : leave, status %d", __FUNCTION__, urb->status);
+}
+
+static void adu_interrupt_out_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct adu_device *dev = urb->context;
+
+ dbg(4," %s : enter, status %d", __FUNCTION__, urb->status);
+ adu_debug_data(5,__FUNCTION__, urb->actual_length, urb->transfer_buffer);
+
+ if (urb->status != 0) {
+ if ((urb->status != -ENOENT) &&
+ (urb->status != -ECONNRESET)) {
+ dbg(1, " %s :nonzero status received: %d",
+ __FUNCTION__, urb->status);
+ }
+ goto exit;
+ }
+
+ wake_up_interruptible(&dev->write_wait);
+exit:
+
+ adu_debug_data(5, __FUNCTION__, urb->actual_length,
+ urb->transfer_buffer);
+ dbg(4," %s : leave, status %d", __FUNCTION__, urb->status);
+}
+
+static int adu_open(struct inode *inode, struct file *file)
+{
+ struct adu_device *dev = NULL;
+ struct usb_interface *interface;
+ int subminor;
+ int retval = 0;
+
+ dbg(2,"%s : enter", __FUNCTION__);
+
+ subminor = iminor(inode);
+
+ mutex_lock(&disconnect_mutex);
+
+ interface = usb_find_interface(&adu_driver, subminor);
+ if (!interface) {
+ err("%s - error, can't find device for minor %d",
+ __FUNCTION__, subminor);
+ retval = -ENODEV;
+ goto exit_no_device;
+ }
+
+ dev = usb_get_intfdata(interface);
+ if (!dev) {
+ retval = -ENODEV;
+ goto exit_no_device;
+ }
+
+ /* lock this device */
+ if ((retval = down_interruptible(&dev->sem))) {
+ dbg(2, "%s : sem down failed", __FUNCTION__);
+ goto exit_no_device;
+ }
+
+ /* increment our usage count for the device */
+ ++dev->open_count;
+ dbg(2,"%s : open count %d", __FUNCTION__, dev->open_count);
+
+ /* save device in the file's private structure */
+ file->private_data = dev;
+
+ /* initialize in direction */
+ dev->read_buffer_length = 0;
+
+ /* fixup first read by having urb waiting for it */
+ usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ adu_interrupt_in_callback, dev,
+ dev->interrupt_in_endpoint->bInterval);
+ /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+ dev->read_urb_finished = 0;
+ usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ /* we ignore failure */
+ /* end of fixup for first read */
+
+ up(&dev->sem);
+
+exit_no_device:
+ mutex_unlock(&disconnect_mutex);
+ dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval);
+
+ return retval;
+}
+
+static int adu_release_internal(struct adu_device *dev)
+{
+ int retval = 0;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (dev->udev == NULL) {
+ /* the device was unplugged before the file was released */
+ adu_delete(dev);
+ goto exit;
+ }
+
+ /* decrement our usage count for the device */
+ --dev->open_count;
+ dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
+ if (dev->open_count <= 0) {
+ adu_abort_transfers(dev);
+ dev->open_count = 0;
+ }
+
+exit:
+ dbg(2," %s : leave", __FUNCTION__);
+ return retval;
+}
+
+static int adu_release(struct inode *inode, struct file *file)
+{
+ struct adu_device *dev = NULL;
+ int retval = 0;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (file == NULL) {
+ dbg(1," %s : file is NULL", __FUNCTION__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ dev = file->private_data;
+
+ if (dev == NULL) {
+ dbg(1," %s : object is NULL", __FUNCTION__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* lock our device */
+ down(&dev->sem); /* not interruptible */
+
+ if (dev->open_count <= 0) {
+ dbg(1," %s : device not opened", __FUNCTION__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* do the work */
+ retval = adu_release_internal(dev);
+
+exit:
+ up(&dev->sem);
+ dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+ return retval;
+}
+
+static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct adu_device *dev;
+ size_t bytes_read = 0;
+ size_t bytes_to_read = count;
+ int i;
+ int retval = 0;
+ int timeout = 0;
+ int should_submit = 0;
+ unsigned long flags;
+ DECLARE_WAITQUEUE(wait, current);
+
+ dbg(2," %s : enter, count = %Zd, file=%p", __FUNCTION__, count, file);
+
+ dev = file->private_data;
+ dbg(2," %s : dev=%p", __FUNCTION__, dev);
+ /* lock this object */
+ if (down_interruptible(&dev->sem))
+ return -ERESTARTSYS;
+
+ /* verify that the device wasn't unplugged */
+ if (dev->udev == NULL || dev->minor == 0) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d", retval);
+ goto exit;
+ }
+
+ /* verify that some data was requested */
+ if (count == 0) {
+ dbg(1," %s : read request of 0 bytes", __FUNCTION__);
+ goto exit;
+ }
+
+ timeout = COMMAND_TIMEOUT;
+ dbg(2," %s : about to start looping", __FUNCTION__);
+ while (bytes_to_read) {
+ int data_in_secondary = dev->secondary_tail - dev->secondary_head;
+ dbg(2," %s : while, data_in_secondary=%d, status=%d",
+ __FUNCTION__, data_in_secondary,
+ dev->interrupt_in_urb->status);
+
+ if (data_in_secondary) {
+ /* drain secondary buffer */
+ int amount = bytes_to_read < data_in_secondary ? bytes_to_read : data_in_secondary;
+ i = copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount);
+ if (i < 0) {
+ retval = -EFAULT;
+ goto exit;
+ }
+ dev->secondary_head += (amount - i);
+ bytes_read += (amount - i);
+ bytes_to_read -= (amount - i);
+ if (i) {
+ retval = bytes_read ? bytes_read : -EFAULT;
+ goto exit;
+ }
+ } else {
+ /* we check the primary buffer */
+ spin_lock_irqsave (&dev->buflock, flags);
+ if (dev->read_buffer_length) {
+ /* we secure access to the primary */
+ char *tmp;
+ dbg(2," %s : swap, read_buffer_length = %d",
+ __FUNCTION__, dev->read_buffer_length);
+ tmp = dev->read_buffer_secondary;
+ dev->read_buffer_secondary = dev->read_buffer_primary;
+ dev->read_buffer_primary = tmp;
+ dev->secondary_head = 0;
+ dev->secondary_tail = dev->read_buffer_length;
+ dev->read_buffer_length = 0;
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ /* we have a free buffer so use it */
+ should_submit = 1;
+ } else {
+ /* even the primary was empty - we may need to do IO */
+ if (dev->interrupt_in_urb->status == -EINPROGRESS) {
+ /* somebody is doing IO */
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ dbg(2," %s : submitted already", __FUNCTION__);
+ } else {
+ /* we must initiate input */
+ dbg(2," %s : initiate input", __FUNCTION__);
+ dev->read_urb_finished = 0;
+
+ usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ adu_interrupt_in_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ if (!retval) {
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ dbg(2," %s : submitted OK", __FUNCTION__);
+ } else {
+ if (retval == -ENOMEM) {
+ retval = bytes_read ? bytes_read : -ENOMEM;
+ }
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ dbg(2," %s : submit failed", __FUNCTION__);
+ goto exit;
+ }
+ }
+
+ /* we wait for I/O to complete */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&dev->read_wait, &wait);
+ if (!dev->read_urb_finished)
+ timeout = schedule_timeout(COMMAND_TIMEOUT);
+ else
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dev->read_wait, &wait);
+
+ if (timeout <= 0) {
+ dbg(2," %s : timeout", __FUNCTION__);
+ retval = bytes_read ? bytes_read : -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (signal_pending(current)) {
+ dbg(2," %s : signal pending", __FUNCTION__);
+ retval = bytes_read ? bytes_read : -EINTR;
+ goto exit;
+ }
+ }
+ }
+ }
+
+ retval = bytes_read;
+ /* if the primary buffer is empty then use it */
+ if (should_submit && !dev->interrupt_in_urb->status==-EINPROGRESS) {
+ usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ adu_interrupt_in_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+ /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+ dev->read_urb_finished = 0;
+ usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ /* we ignore failure */
+ }
+
+exit:
+ /* unlock the device */
+ up(&dev->sem);
+
+ dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+ return retval;
+}
+
+static ssize_t adu_write(struct file *file, const __user char *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct adu_device *dev;
+ size_t bytes_written = 0;
+ size_t bytes_to_write;
+ size_t buffer_size;
+ int retval = 0;
+ int timeout = 0;
+
+ dbg(2," %s : enter, count = %Zd", __FUNCTION__, count);
+
+ dev = file->private_data;
+
+ /* lock this object */
+ down_interruptible(&dev->sem);
+
+ /* verify that the device wasn't unplugged */
+ if (dev->udev == NULL || dev->minor == 0) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d", retval);
+ goto exit;
+ }
+
+ /* verify that we actually have some data to write */
+ if (count == 0) {
+ dbg(1," %s : write request of 0 bytes", __FUNCTION__);
+ goto exit;
+ }
+
+
+ while (count > 0) {
+ if (dev->interrupt_out_urb->status == -EINPROGRESS) {
+ timeout = COMMAND_TIMEOUT;
+
+ while (timeout > 0) {
+ if (signal_pending(current)) {
+ dbg(1," %s : interrupted", __FUNCTION__);
+ retval = -EINTR;
+ goto exit;
+ }
+ up(&dev->sem);
+ timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout);
+ down_interruptible(&dev->sem);
+ if (timeout > 0) {
+ break;
+ }
+ dbg(1," %s : interrupted timeout: %d", __FUNCTION__, timeout);
+ }
+
+
+ dbg(1," %s : final timeout: %d", __FUNCTION__, timeout);
+
+ if (timeout == 0) {
+ dbg(1, "%s - command timed out.", __FUNCTION__);
+ retval = -ETIMEDOUT;
+ goto exit;
+ }
+
+ dbg(4," %s : in progress, count = %Zd", __FUNCTION__, count);
+
+ } else {
+ dbg(4," %s : sending, count = %Zd", __FUNCTION__, count);
+
+ /* write the data into interrupt_out_buffer from userspace */
+ buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+ bytes_to_write = count > buffer_size ? buffer_size : count;
+ dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd",
+ __FUNCTION__, buffer_size, count, bytes_to_write);
+
+ if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ /* send off the urb */
+ usb_fill_int_urb(
+ dev->interrupt_out_urb,
+ dev->udev,
+ usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+ dev->interrupt_out_buffer,
+ bytes_to_write,
+ adu_interrupt_out_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+ /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+ dev->interrupt_out_urb->actual_length = bytes_to_write;
+ retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
+ if (retval < 0) {
+ err("Couldn't submit interrupt_out_urb %d", retval);
+ goto exit;
+ }
+
+ buffer += bytes_to_write;
+ count -= bytes_to_write;
+
+ bytes_written += bytes_to_write;
+ }
+ }
+
+ retval = bytes_written;
+
+exit:
+ /* unlock the device */
+ up(&dev->sem);
+
+ dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+
+ return retval;
+}
+
+/* file operations needed when we register this driver */
+static struct file_operations adu_fops = {
+ .owner = THIS_MODULE,
+ .read = adu_read,
+ .write = adu_write,
+ .open = adu_open,
+ .release = adu_release,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver adu_class = {
+ .name = "usb/adutux%d",
+ .fops = &adu_fops,
+ .minor_base = ADU_MINOR_BASE,
+};
+
+/**
+ * adu_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int adu_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct adu_device *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int retval = -ENODEV;
+ int in_end_size;
+ int out_end_size;
+ int i;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (udev == NULL) {
+ dev_err(&interface->dev, "udev is NULL.\n");
+ goto exit;
+ }
+
+ /* allocate memory for our device state and intialize it */
+ dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ init_MUTEX(&dev->sem);
+ spin_lock_init(&dev->buflock);
+ dev->udev = udev;
+ init_waitqueue_head(&dev->read_wait);
+ init_waitqueue_head(&dev->write_wait);
+
+ iface_desc = &interface->altsetting[0];
+
+ /* set up the endpoint information */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint))
+ dev->interrupt_in_endpoint = endpoint;
+
+ if (usb_endpoint_is_int_out(endpoint))
+ dev->interrupt_out_endpoint = endpoint;
+ }
+ if (dev->interrupt_in_endpoint == NULL) {
+ dev_err(&interface->dev, "interrupt in endpoint not found\n");
+ goto error;
+ }
+ if (dev->interrupt_out_endpoint == NULL) {
+ dev_err(&interface->dev, "interrupt out endpoint not found\n");
+ goto error;
+ }
+
+ in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
+ out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+
+ dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL);
+ if (!dev->read_buffer_primary) {
+ dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /* debug code prime the buffer */
+ memset(dev->read_buffer_primary, 'a', in_end_size);
+ memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size);
+ memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size);
+ memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size);
+
+ dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL);
+ if (!dev->read_buffer_secondary) {
+ dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /* debug code prime the buffer */
+ memset(dev->read_buffer_secondary, 'e', in_end_size);
+ memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size);
+ memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size);
+ memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size);
+
+ dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL);
+ if (!dev->interrupt_in_buffer) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n");
+ goto error;
+ }
+
+ /* debug code prime the buffer */
+ memset(dev->interrupt_in_buffer, 'i', in_end_size);
+
+ dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_in_urb) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+ goto error;
+ }
+ dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL);
+ if (!dev->interrupt_out_buffer) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n");
+ goto error;
+ }
+ dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_out_urb) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n");
+ goto error;
+ }
+
+ if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number,
+ sizeof(dev->serial_number))) {
+ dev_err(&interface->dev, "Could not retrieve serial number\n");
+ goto error;
+ }
+ dbg(2," %s : serial_number=%s", __FUNCTION__, dev->serial_number);
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(interface, dev);
+
+ retval = usb_register_dev(interface, &adu_class);
+
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(&interface->dev, "Not able to get a minor for this device.\n");
+ usb_set_intfdata(interface, NULL);
+ goto error;
+ }
+
+ dev->minor = interface->minor;
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d",
+ udev->descriptor.idProduct, dev->serial_number,
+ (dev->minor - ADU_MINOR_BASE));
+exit:
+ dbg(2," %s : leave, return value %p (dev)", __FUNCTION__, dev);
+
+ return retval;
+
+error:
+ adu_delete(dev);
+ return retval;
+}
+
+/**
+ * adu_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ */
+static void adu_disconnect(struct usb_interface *interface)
+{
+ struct adu_device *dev;
+ int minor;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ mutex_lock(&disconnect_mutex); /* not interruptible */
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ down(&dev->sem); /* not interruptible */
+
+ minor = dev->minor;
+
+ /* give back our minor */
+ usb_deregister_dev(interface, &adu_class);
+ dev->minor = 0;
+
+ /* if the device is not opened, then we clean up right now */
+ dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
+ if (!dev->open_count) {
+ up(&dev->sem);
+ adu_delete(dev);
+ } else {
+ dev->udev = NULL;
+ up(&dev->sem);
+ }
+
+ mutex_unlock(&disconnect_mutex);
+
+ dev_info(&interface->dev, "ADU device adutux%d now disconnected",
+ (minor - ADU_MINOR_BASE));
+
+ dbg(2," %s : leave", __FUNCTION__);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver adu_driver = {
+ .name = "adutux",
+ .probe = adu_probe,
+ .disconnect = adu_disconnect,
+ .id_table = device_table,
+};
+
+static int __init adu_init(void)
+{
+ int result;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&adu_driver);
+ if (result < 0) {
+ err("usb_register failed for the "__FILE__" driver. "
+ "Error number %d", result);
+ goto exit;
+ }
+
+ info("adutux " DRIVER_DESC " " DRIVER_VERSION);
+ info("adutux is an experimental driver. Use at your own risk");
+
+exit:
+ dbg(2," %s : leave, return value %d", __FUNCTION__, result);
+
+ return result;
+}
+
+static void __exit adu_exit(void)
+{
+ dbg(2," %s : enter", __FUNCTION__);
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&adu_driver);
+ dbg(2," %s : leave", __FUNCTION__);
+}
+
+module_init(adu_init);
+module_exit(adu_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index 1fef36e71c5..4fd2110b341 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -806,7 +806,7 @@ static void auerbuf_releasebuf( pauerbuf_t bp)
0 Initial, OK
-EINPROGRESS during submission until end
-ENOENT if urb is unlinked
--ETIMEDOUT Transfer timed out, NAK
+-ETIME Device did not respond
-ENOMEM Memory Overflow
-ENODEV Specified USB-device or bus doesn't exist
-ENXIO URB already queued
@@ -832,7 +832,7 @@ static int auerswald_status_retry (int status)
{
switch (status) {
case 0:
- case -ETIMEDOUT:
+ case -ETIME:
case -EOVERFLOW:
case -EAGAIN:
case -EPIPE:
@@ -1858,7 +1858,7 @@ static int auerchar_release (struct inode *inode, struct file *file)
/*----------------------------------------------------------------------*/
/* File operation structure */
-static struct file_operations auerswald_fops =
+static const struct file_operations auerswald_fops =
{
.owner = THIS_MODULE,
.llseek = no_llseek,
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index 9c46746d5d0..b63b5f34b2a 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -209,7 +209,7 @@ static int cypress_probe(struct usb_interface *interface,
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory!\n");
- goto error;
+ goto error_mem;
}
dev->udev = usb_get_dev(interface_to_usbdev(interface));
@@ -218,15 +218,26 @@ static int cypress_probe(struct usb_interface *interface,
usb_set_intfdata(interface, dev);
/* create device attribute files */
- device_create_file(&interface->dev, &dev_attr_port0);
- device_create_file(&interface->dev, &dev_attr_port1);
+ retval = device_create_file(&interface->dev, &dev_attr_port0);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_port1);
+ if (retval)
+ goto error;
/* let the user know that the device is now attached */
dev_info(&interface->dev,
"Cypress CY7C63xxx device now attached\n");
+ return 0;
- retval = 0;
error:
+ device_remove_file(&interface->dev, &dev_attr_port0);
+ device_remove_file(&interface->dev, &dev_attr_port1);
+ usb_set_intfdata(interface, NULL);
+ usb_put_dev(dev->udev);
+ kfree(dev);
+
+error_mem:
return retval;
}
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index b20bec44555..04e87acd6e4 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -353,7 +353,7 @@ static int cytherm_probe(struct usb_interface *interface,
dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
if (dev == NULL) {
dev_err (&interface->dev, "Out of memory\n");
- goto error;
+ goto error_mem;
}
dev->udev = usb_get_dev(udev);
@@ -362,18 +362,35 @@ static int cytherm_probe(struct usb_interface *interface,
dev->brightness = 0xFF;
- device_create_file(&interface->dev, &dev_attr_brightness);
- device_create_file(&interface->dev, &dev_attr_temp);
- device_create_file(&interface->dev, &dev_attr_button);
- device_create_file(&interface->dev, &dev_attr_port0);
- device_create_file(&interface->dev, &dev_attr_port1);
+ retval = device_create_file(&interface->dev, &dev_attr_brightness);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_temp);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_button);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_port0);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_port1);
+ if (retval)
+ goto error;
- dev_info (&interface->dev,
+ dev_info (&interface->dev,
"Cypress thermometer device now attached\n");
return 0;
-
- error:
+error:
+ device_remove_file(&interface->dev, &dev_attr_brightness);
+ device_remove_file(&interface->dev, &dev_attr_temp);
+ device_remove_file(&interface->dev, &dev_attr_button);
+ device_remove_file(&interface->dev, &dev_attr_port0);
+ device_remove_file(&interface->dev, &dev_attr_port1);
+ usb_set_intfdata (interface, NULL);
+ usb_put_dev(dev->udev);
kfree(dev);
+error_mem:
return retval;
}
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
new file mode 100644
index 00000000000..b88a09497c2
--- /dev/null
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -0,0 +1,2809 @@
+/*
+* USB FTDI client driver for Elan Digital Systems's Uxxx adapters
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.com
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB client drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+MODULE_AUTHOR("Tony Olech");
+MODULE_DESCRIPTION("FTDI ELAN driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+extern struct platform_driver u132_platform_driver;
+static struct workqueue_struct *status_queue;
+static struct workqueue_struct *command_queue;
+static struct workqueue_struct *respond_queue;
+/*
+* ftdi_module_lock exists to protect access to global variables
+*
+*/
+static struct semaphore ftdi_module_lock;
+static int ftdi_instances = 0;
+static struct list_head ftdi_static_list;
+/*
+* end of the global variables protected by ftdi_module_lock
+*/
+#include "usb_u132.h"
+#define TD_DEVNOTRESP 5
+/* Define these values to match your devices*/
+#define USB_FTDI_ELAN_VENDOR_ID 0x0403
+#define USB_FTDI_ELAN_PRODUCT_ID 0xd6ea
+/* table of devices that work with this driver*/
+static struct usb_device_id ftdi_elan_table[] = {
+ {USB_DEVICE(USB_FTDI_ELAN_VENDOR_ID, USB_FTDI_ELAN_PRODUCT_ID)},
+ { /* Terminating entry */ }
+};
+
+MODULE_DEVICE_TABLE(usb, ftdi_elan_table);
+/* only the jtag(firmware upgrade device) interface requires
+* a device file and corresponding minor number, but the
+* interface is created unconditionally - I suppose it could
+* be configured or not according to a module parameter.
+* But since we(now) require one interface per device,
+* and since it unlikely that a normal installation would
+* require more than a couple of elan-ftdi devices, 8 seems
+* like a reasonable limit to have here, and if someone
+* really requires more than 8 devices, then they can frig the
+* code and recompile
+*/
+#define USB_FTDI_ELAN_MINOR_BASE 192
+#define COMMAND_BITS 5
+#define COMMAND_SIZE (1<<COMMAND_BITS)
+#define COMMAND_MASK (COMMAND_SIZE-1)
+struct u132_command {
+ u8 header;
+ u16 length;
+ u8 address;
+ u8 width;
+ u32 value;
+ int follows;
+ void *buffer;
+};
+#define RESPOND_BITS 5
+#define RESPOND_SIZE (1<<RESPOND_BITS)
+#define RESPOND_MASK (RESPOND_SIZE-1)
+struct u132_respond {
+ u8 header;
+ u8 address;
+ u32 *value;
+ int *result;
+ struct completion wait_completion;
+};
+struct u132_target {
+ void *endp;
+ struct urb *urb;
+ int toggle_bits;
+ int error_count;
+ int condition_code;
+ int repeat_number;
+ int halted;
+ int skipped;
+ int actual;
+ int non_null;
+ int active;
+ int abandoning;
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual,
+ int non_null);
+};
+/* Structure to hold all of our device specific stuff*/
+struct usb_ftdi {
+ struct list_head ftdi_list;
+ struct semaphore u132_lock;
+ int command_next;
+ int command_head;
+ struct u132_command command[COMMAND_SIZE];
+ int respond_next;
+ int respond_head;
+ struct u132_respond respond[RESPOND_SIZE];
+ struct u132_target target[4];
+ char device_name[16];
+ unsigned synchronized:1;
+ unsigned enumerated:1;
+ unsigned registered:1;
+ unsigned initialized:1;
+ unsigned card_ejected:1;
+ int function;
+ int sequence_num;
+ int disconnected;
+ int gone_away;
+ int stuck_status;
+ int status_queue_delay;
+ struct semaphore sw_lock;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ struct usb_class_driver *class;
+ struct work_struct status_work;
+ struct work_struct command_work;
+ struct work_struct respond_work;
+ struct u132_platform_data platform_data;
+ struct resource resources[0];
+ struct platform_device platform_dev;
+ unsigned char *bulk_in_buffer;
+ size_t bulk_in_size;
+ size_t bulk_in_last;
+ size_t bulk_in_left;
+ __u8 bulk_in_endpointAddr;
+ __u8 bulk_out_endpointAddr;
+ struct kref kref;
+ u32 controlreg;
+ u8 response[4 + 1024];
+ int expected;
+ int recieved;
+ int ed_found;
+};
+#define kref_to_usb_ftdi(d) container_of(d, struct usb_ftdi, kref)
+#define platform_device_to_usb_ftdi(d) container_of(d, struct usb_ftdi, \
+ platform_dev)
+static struct usb_driver ftdi_elan_driver;
+static void ftdi_elan_delete(struct kref *kref)
+{
+ struct usb_ftdi *ftdi = kref_to_usb_ftdi(kref);
+ dev_warn(&ftdi->udev->dev, "FREEING ftdi=%p\n", ftdi);
+ usb_put_dev(ftdi->udev);
+ ftdi->disconnected += 1;
+ down(&ftdi_module_lock);
+ list_del_init(&ftdi->ftdi_list);
+ ftdi_instances -= 1;
+ up(&ftdi_module_lock);
+ kfree(ftdi->bulk_in_buffer);
+ ftdi->bulk_in_buffer = NULL;
+}
+
+static void ftdi_elan_put_kref(struct usb_ftdi *ftdi)
+{
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_elan_get_kref(struct usb_ftdi *ftdi)
+{
+ kref_get(&ftdi->kref);
+}
+
+static void ftdi_elan_init_kref(struct usb_ftdi *ftdi)
+{
+ kref_init(&ftdi->kref);
+}
+
+static void ftdi_status_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+ return;
+ } else if (queue_work(status_queue, &ftdi->status_work))
+ return;
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+ return;
+}
+
+static void ftdi_status_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+ kref_get(&ftdi->kref);
+ } else if (queue_work(status_queue, &ftdi->status_work))
+ kref_get(&ftdi->kref);
+ return;
+}
+
+static void ftdi_status_cancel_work(struct usb_ftdi *ftdi)
+{
+ if (cancel_delayed_work(&ftdi->status_work))
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_command_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(command_queue, &ftdi->command_work,
+ delta))
+ return;
+ } else if (queue_work(command_queue, &ftdi->command_work))
+ return;
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+ return;
+}
+
+static void ftdi_command_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(command_queue, &ftdi->command_work,
+ delta))
+ kref_get(&ftdi->kref);
+ } else if (queue_work(command_queue, &ftdi->command_work))
+ kref_get(&ftdi->kref);
+ return;
+}
+
+static void ftdi_command_cancel_work(struct usb_ftdi *ftdi)
+{
+ if (cancel_delayed_work(&ftdi->command_work))
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_response_requeue_work(struct usb_ftdi *ftdi,
+ unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(respond_queue, &ftdi->respond_work,
+ delta))
+ return;
+ } else if (queue_work(respond_queue, &ftdi->respond_work))
+ return;
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+ return;
+}
+
+static void ftdi_respond_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(respond_queue, &ftdi->respond_work,
+ delta))
+ kref_get(&ftdi->kref);
+ } else if (queue_work(respond_queue, &ftdi->respond_work))
+ kref_get(&ftdi->kref);
+ return;
+}
+
+static void ftdi_response_cancel_work(struct usb_ftdi *ftdi)
+{
+ if (cancel_delayed_work(&ftdi->respond_work))
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+void ftdi_elan_gone_away(struct platform_device *pdev)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ ftdi->gone_away += 1;
+ ftdi_elan_put_kref(ftdi);
+}
+
+
+EXPORT_SYMBOL_GPL(ftdi_elan_gone_away);
+void ftdi_release_platform_dev(struct device *dev)
+{
+ dev->parent = NULL;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+ struct u132_target *target, u8 *buffer, int length);
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi);
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi);
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi);
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi);
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_hcd_init(struct usb_ftdi *ftdi)
+{
+ int result;
+ if (ftdi->platform_dev.dev.parent)
+ return -EBUSY;
+ ftdi_elan_get_kref(ftdi);
+ ftdi->platform_data.potpg = 100;
+ ftdi->platform_data.reset = NULL;
+ ftdi->platform_dev.id = ftdi->sequence_num;
+ ftdi->platform_dev.resource = ftdi->resources;
+ ftdi->platform_dev.num_resources = ARRAY_SIZE(ftdi->resources);
+ ftdi->platform_dev.dev.platform_data = &ftdi->platform_data;
+ ftdi->platform_dev.dev.parent = NULL;
+ ftdi->platform_dev.dev.release = ftdi_release_platform_dev;
+ ftdi->platform_dev.dev.dma_mask = NULL;
+ snprintf(ftdi->device_name, sizeof(ftdi->device_name), "u132_hcd");
+ ftdi->platform_dev.name = ftdi->device_name;
+ dev_info(&ftdi->udev->dev, "requesting module '%s'\n", "u132_hcd");
+ request_module("u132_hcd");
+ dev_info(&ftdi->udev->dev, "registering '%s'\n",
+ ftdi->platform_dev.name);
+ result = platform_device_register(&ftdi->platform_dev);
+ return result;
+}
+
+static void ftdi_elan_abandon_completions(struct usb_ftdi *ftdi)
+{
+ down(&ftdi->u132_lock);
+ while (ftdi->respond_next > ftdi->respond_head) {
+ struct u132_respond *respond = &ftdi->respond[RESPOND_MASK &
+ ftdi->respond_head++];
+ *respond->result = -ESHUTDOWN;
+ *respond->value = 0;
+ complete(&respond->wait_completion);
+ } up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_abandon_targets(struct usb_ftdi *ftdi)
+{
+ int ed_number = 4;
+ down(&ftdi->u132_lock);
+ while (ed_number-- > 0) {
+ struct u132_target *target = &ftdi->target[ed_number];
+ if (target->active == 1) {
+ target->condition_code = TD_DEVNOTRESP;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, NULL, 0);
+ down(&ftdi->u132_lock);
+ }
+ }
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_flush_targets(struct usb_ftdi *ftdi)
+{
+ int ed_number = 4;
+ down(&ftdi->u132_lock);
+ while (ed_number-- > 0) {
+ struct u132_target *target = &ftdi->target[ed_number];
+ target->abandoning = 1;
+ wait_1:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x80 | (ed_number << 5) | 0x4;
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait_1;
+ }
+ }
+ wait_2:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x90 | (ed_number << 5);
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait_2;
+ }
+ }
+ }
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_cancel_targets(struct usb_ftdi *ftdi)
+{
+ int ed_number = 4;
+ down(&ftdi->u132_lock);
+ while (ed_number-- > 0) {
+ struct u132_target *target = &ftdi->target[ed_number];
+ target->abandoning = 1;
+ wait:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x80 | (ed_number << 5) | 0x4;
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait;
+ }
+ }
+ }
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi)
+{
+ ftdi_command_queue_work(ftdi, 0);
+ return;
+}
+
+static void ftdi_elan_command_work(void *data)
+{
+ struct usb_ftdi *ftdi = data;
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else {
+ int retval = ftdi_elan_command_engine(ftdi);
+ if (retval == -ESHUTDOWN) {
+ ftdi->disconnected += 1;
+ } else if (retval == -ENODEV) {
+ ftdi->disconnected += 1;
+ } else if (retval)
+ dev_err(&ftdi->udev->dev, "command error %d\n", retval);
+ ftdi_command_requeue_work(ftdi, msecs_to_jiffies(10));
+ return;
+ }
+}
+
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi)
+{
+ ftdi_respond_queue_work(ftdi, 0);
+ return;
+}
+
+static void ftdi_elan_respond_work(void *data)
+{
+ struct usb_ftdi *ftdi = data;
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else {
+ int retval = ftdi_elan_respond_engine(ftdi);
+ if (retval == 0) {
+ } else if (retval == -ESHUTDOWN) {
+ ftdi->disconnected += 1;
+ } else if (retval == -ENODEV) {
+ ftdi->disconnected += 1;
+ } else if (retval == -ENODEV) {
+ ftdi->disconnected += 1;
+ } else if (retval == -EILSEQ) {
+ ftdi->disconnected += 1;
+ } else {
+ ftdi->disconnected += 1;
+ dev_err(&ftdi->udev->dev, "respond error %d\n", retval);
+ }
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_abandon_completions(ftdi);
+ ftdi_elan_abandon_targets(ftdi);
+ }
+ ftdi_response_requeue_work(ftdi, msecs_to_jiffies(10));
+ return;
+ }
+}
+
+
+/*
+* the sw_lock is initially held and will be freed
+* after the FTDI has been synchronized
+*
+*/
+static void ftdi_elan_status_work(void *data)
+{
+ struct usb_ftdi *ftdi = data;
+ int work_delay_in_msec = 0;
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else if (ftdi->synchronized == 0) {
+ down(&ftdi->sw_lock);
+ if (ftdi_elan_synchronize(ftdi) == 0) {
+ ftdi->synchronized = 1;
+ ftdi_command_queue_work(ftdi, 1);
+ ftdi_respond_queue_work(ftdi, 1);
+ up(&ftdi->sw_lock);
+ work_delay_in_msec = 100;
+ } else {
+ dev_err(&ftdi->udev->dev, "synchronize failed\n");
+ up(&ftdi->sw_lock);
+ work_delay_in_msec = 10 *1000;
+ }
+ } else if (ftdi->stuck_status > 0) {
+ if (ftdi_elan_stuck_waiting(ftdi) == 0) {
+ ftdi->stuck_status = 0;
+ ftdi->synchronized = 0;
+ } else if ((ftdi->stuck_status++ % 60) == 1) {
+ dev_err(&ftdi->udev->dev, "WRONG type of card inserted "
+ "- please remove\n");
+ } else
+ dev_err(&ftdi->udev->dev, "WRONG type of card inserted "
+ "- checked %d times\n", ftdi->stuck_status);
+ work_delay_in_msec = 100;
+ } else if (ftdi->enumerated == 0) {
+ if (ftdi_elan_enumeratePCI(ftdi) == 0) {
+ ftdi->enumerated = 1;
+ work_delay_in_msec = 250;
+ } else
+ work_delay_in_msec = 1000;
+ } else if (ftdi->initialized == 0) {
+ if (ftdi_elan_setupOHCI(ftdi) == 0) {
+ ftdi->initialized = 1;
+ work_delay_in_msec = 500;
+ } else {
+ dev_err(&ftdi->udev->dev, "initialized failed - trying "
+ "again in 10 seconds\n");
+ work_delay_in_msec = 10 *1000;
+ }
+ } else if (ftdi->registered == 0) {
+ work_delay_in_msec = 10;
+ if (ftdi_elan_hcd_init(ftdi) == 0) {
+ ftdi->registered = 1;
+ } else
+ dev_err(&ftdi->udev->dev, "register failed\n");
+ work_delay_in_msec = 250;
+ } else {
+ if (ftdi_elan_checkingPCI(ftdi) == 0) {
+ work_delay_in_msec = 250;
+ } else if (ftdi->controlreg & 0x00400000) {
+ if (ftdi->gone_away > 0) {
+ dev_err(&ftdi->udev->dev, "PCI device eject con"
+ "firmed platform_dev.dev.parent=%p plat"
+ "form_dev.dev=%p\n",
+ ftdi->platform_dev.dev.parent,
+ &ftdi->platform_dev.dev);
+ platform_device_unregister(&ftdi->platform_dev);
+ ftdi->platform_dev.dev.parent = NULL;
+ ftdi->registered = 0;
+ ftdi->enumerated = 0;
+ ftdi->card_ejected = 0;
+ ftdi->initialized = 0;
+ ftdi->gone_away = 0;
+ } else
+ ftdi_elan_flush_targets(ftdi);
+ work_delay_in_msec = 250;
+ } else {
+ dev_err(&ftdi->udev->dev, "PCI device has disappeared\n"
+ );
+ ftdi_elan_cancel_targets(ftdi);
+ work_delay_in_msec = 500;
+ ftdi->enumerated = 0;
+ ftdi->initialized = 0;
+ }
+ }
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else {
+ ftdi_status_requeue_work(ftdi,
+ msecs_to_jiffies(work_delay_in_msec));
+ return;
+ }
+}
+
+
+/*
+* file_operations for the jtag interface
+*
+* the usage count for the device is incremented on open()
+* and decremented on release()
+*/
+static int ftdi_elan_open(struct inode *inode, struct file *file)
+{
+ int subminor = iminor(inode);
+ struct usb_interface *interface = usb_find_interface(&ftdi_elan_driver,
+ subminor);
+ if (!interface) {
+ printk(KERN_ERR "can't find device for minor %d\n", subminor);
+ return -ENODEV;
+ } else {
+ struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+ if (!ftdi) {
+ return -ENODEV;
+ } else {
+ if (down_interruptible(&ftdi->sw_lock)) {
+ return -EINTR;
+ } else {
+ ftdi_elan_get_kref(ftdi);
+ file->private_data = ftdi;
+ return 0;
+ }
+ }
+ }
+}
+
+static int ftdi_elan_release(struct inode *inode, struct file *file)
+{
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ if (ftdi == NULL)
+ return -ENODEV;
+ up(&ftdi->sw_lock); /* decrement the count on our device */
+ ftdi_elan_put_kref(ftdi);
+ return 0;
+}
+
+
+#define FTDI_ELAN_IOC_MAGIC 0xA1
+#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132)
+static int ftdi_elan_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case FTDI_ELAN_IOCDEBUG:{
+ char line[132];
+ int size = strncpy_from_user(line,
+ (const char __user *)arg, sizeof(line));
+ if (size < 0) {
+ return -EINVAL;
+ } else {
+ printk(KERN_ERR "TODO: ioctl %s\n", line);
+ return 0;
+ }
+ }
+ default:
+ return -EFAULT;
+ }
+}
+
+
+/*
+*
+* blocking bulk reads are used to get data from the device
+*
+*/
+static ssize_t ftdi_elan_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char data[30 *3 + 4];
+ char *d = data;
+ int m = (sizeof(data) - 1) / 3;
+ int bytes_read = 0;
+ int retry_on_empty = 10;
+ int retry_on_timeout = 5;
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ }
+ data[0] = 0;
+ have:if (ftdi->bulk_in_left > 0) {
+ if (count-- > 0) {
+ char *p = ++ftdi->bulk_in_last + ftdi->bulk_in_buffer;
+ ftdi->bulk_in_left -= 1;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X", 0x000000FF & *p);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ if (copy_to_user(buffer++, p, 1)) {
+ return -EFAULT;
+ } else {
+ bytes_read += 1;
+ goto have;
+ }
+ } else
+ return bytes_read;
+ }
+ more:if (count > 0) {
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(50));
+ if (packet_bytes > 2) {
+ ftdi->bulk_in_left = packet_bytes - 2;
+ ftdi->bulk_in_last = 1;
+ goto have;
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto more;
+ } else if (bytes_read > 0) {
+ return bytes_read;
+ } else
+ return retval;
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return bytes_read;
+ } else
+ return retval;
+ } else
+ return bytes_read;
+}
+
+static void ftdi_elan_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)urb->context;
+ if (urb->status && !(urb->status == -ENOENT || urb->status ==
+ -ECONNRESET || urb->status == -ESHUTDOWN)) {
+ dev_err(&ftdi->udev->dev, "urb=%p write bulk status received: %"
+ "d\n", urb, urb->status);
+ }
+ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+}
+
+static int fill_buffer_with_all_queued_commands(struct usb_ftdi *ftdi,
+ char *buf, int command_size, int total_size)
+{
+ int ed_commands = 0;
+ int b = 0;
+ int I = command_size;
+ int i = ftdi->command_head;
+ while (I-- > 0) {
+ struct u132_command *command = &ftdi->command[COMMAND_MASK &
+ i++];
+ int F = command->follows;
+ u8 *f = command->buffer;
+ if (command->header & 0x80) {
+ ed_commands |= 1 << (0x3 & (command->header >> 5));
+ }
+ buf[b++] = command->header;
+ buf[b++] = (command->length >> 0) & 0x00FF;
+ buf[b++] = (command->length >> 8) & 0x00FF;
+ buf[b++] = command->address;
+ buf[b++] = command->width;
+ while (F-- > 0) {
+ buf[b++] = *f++;
+ }
+ }
+ return ed_commands;
+}
+
+static int ftdi_elan_total_command_size(struct usb_ftdi *ftdi, int command_size)
+{
+ int total_size = 0;
+ int I = command_size;
+ int i = ftdi->command_head;
+ while (I-- > 0) {
+ struct u132_command *command = &ftdi->command[COMMAND_MASK &
+ i++];
+ total_size += 5 + command->follows;
+ } return total_size;
+}
+
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
+{
+ int retval;
+ char *buf;
+ int ed_commands;
+ int total_size;
+ struct urb *urb;
+ int command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size == 0)
+ return 0;
+ total_size = ftdi_elan_total_command_size(ftdi, command_size);
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&ftdi->udev->dev, "could not get a urb to write %d comm"
+ "ands totaling %d bytes to the Uxxx\n", command_size,
+ total_size);
+ return -ENOMEM;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, total_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ dev_err(&ftdi->udev->dev, "could not get a buffer to write %d c"
+ "ommands totaling %d bytes to the Uxxx\n", command_size,
+ total_size);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ ed_commands = fill_buffer_with_all_queued_commands(ftdi, buf,
+ command_size, total_size);
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, total_size,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ if (ed_commands) {
+ char diag[40 *3 + 4];
+ char *d = diag;
+ int m = total_size;
+ u8 *c = buf;
+ int s = (sizeof(diag) - 1) / 3;
+ diag[0] = 0;
+ while (s-- > 0 && m-- > 0) {
+ if (s > 0 || m == 0) {
+ d += sprintf(d, " %02X", *c++);
+ } else
+ d += sprintf(d, " ..");
+ }
+ }
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed %d to submit urb %p to write "
+ "%d commands totaling %d bytes to the Uxxx\n", retval,
+ urb, command_size, total_size);
+ usb_buffer_free(ftdi->udev, total_size, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ return retval;
+ }
+ usb_free_urb(urb); /* release our reference to this urb,
+ the USB core will eventually free it entirely */
+ ftdi->command_head += command_size;
+ ftdi_elan_kick_respond_queue(ftdi);
+ return 0;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+ struct u132_target *target, u8 *buffer, int length)
+{
+ struct urb *urb = target->urb;
+ int halted = target->halted;
+ int skipped = target->skipped;
+ int actual = target->actual;
+ int non_null = target->non_null;
+ int toggle_bits = target->toggle_bits;
+ int error_count = target->error_count;
+ int condition_code = target->condition_code;
+ int repeat_number = target->repeat_number;
+ void (*callback) (void *, struct urb *, u8 *, int, int, int, int, int,
+ int, int, int, int) = target->callback;
+ target->active -= 1;
+ target->callback = NULL;
+ (*callback) (target->endp, urb, buffer, length, toggle_bits,
+ error_count, condition_code, repeat_number, halted, skipped,
+ actual, non_null);
+}
+
+static char *have_ed_set_response(struct usb_ftdi *ftdi,
+ struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+ char *b)
+{
+ int payload = (ed_length >> 0) & 0x07FF;
+ down(&ftdi->u132_lock);
+ target->actual = 0;
+ target->non_null = (ed_length >> 15) & 0x0001;
+ target->repeat_number = (ed_length >> 11) & 0x000F;
+ if (ed_type == 0x02) {
+ if (payload == 0 || target->abandoning > 0) {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ } else {
+ ftdi->expected = 4 + payload;
+ ftdi->ed_found = 1;
+ up(&ftdi->u132_lock);
+ return b;
+ }
+ } else if (ed_type == 0x03) {
+ if (payload == 0 || target->abandoning > 0) {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ } else {
+ ftdi->expected = 4 + payload;
+ ftdi->ed_found = 1;
+ up(&ftdi->u132_lock);
+ return b;
+ }
+ } else if (ed_type == 0x01) {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ } else {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ }
+}
+
+static char *have_ed_get_response(struct usb_ftdi *ftdi,
+ struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+ char *b)
+{
+ down(&ftdi->u132_lock);
+ target->condition_code = TD_DEVNOTRESP;
+ target->actual = (ed_length >> 0) & 0x01FF;
+ target->non_null = (ed_length >> 15) & 0x0001;
+ target->repeat_number = (ed_length >> 11) & 0x000F;
+ up(&ftdi->u132_lock);
+ if (target->active)
+ ftdi_elan_do_callback(ftdi, target, NULL, 0);
+ target->abandoning = 0;
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+}
+
+
+/*
+* The engine tries to empty the FTDI fifo
+*
+* all responses found in the fifo data are dispatched thus
+* the response buffer can only ever hold a maximum sized
+* response from the Uxxx.
+*
+*/
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi)
+{
+ u8 *b = ftdi->response + ftdi->recieved;
+ int bytes_read = 0;
+ int retry_on_empty = 1;
+ int retry_on_timeout = 3;
+ int empty_packets = 0;
+ read:{
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(500));
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = packet_bytes;
+ u8 *c = ftdi->bulk_in_buffer;
+ int s = (sizeof(diag) - 1) / 3;
+ diag[0] = 0;
+ while (s-- > 0 && m-- > 0) {
+ if (s > 0 || m == 0) {
+ d += sprintf(d, " %02X", *c++);
+ } else
+ d += sprintf(d, " ..");
+ }
+ if (packet_bytes > 2) {
+ ftdi->bulk_in_left = packet_bytes - 2;
+ ftdi->bulk_in_last = 1;
+ goto have;
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ dev_err(&ftdi->udev->dev, "TIMED OUT with packe"
+ "t_bytes = %d with total %d bytes%s\n",
+ packet_bytes, bytes_read, diag);
+ goto more;
+ } else if (bytes_read > 0) {
+ dev_err(&ftdi->udev->dev, "ONLY %d bytes%s\n",
+ bytes_read, diag);
+ return -ENOMEM;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT with packe"
+ "t_bytes = %d with total %d bytes%s\n",
+ packet_bytes, bytes_read, diag);
+ return -ENOMEM;
+ }
+ } else if (retval == -EILSEQ) {
+ dev_err(&ftdi->udev->dev, "error = %d with packet_bytes"
+ " = %d with total %d bytes%s\n", retval,
+ packet_bytes, bytes_read, diag);
+ return retval;
+ } else if (retval) {
+ dev_err(&ftdi->udev->dev, "error = %d with packet_bytes"
+ " = %d with total %d bytes%s\n", retval,
+ packet_bytes, bytes_read, diag);
+ return retval;
+ } else if (packet_bytes == 2) {
+ unsigned char s0 = ftdi->bulk_in_buffer[0];
+ unsigned char s1 = ftdi->bulk_in_buffer[1];
+ empty_packets += 1;
+ if (s0 == 0x31 && s1 == 0x60) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ } else if (s0 == 0x31 && s1 == 0x00) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ } else {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ }
+ } else if (packet_bytes == 1) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ } else {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ }
+ }
+ more:{
+ goto read;
+ }
+ have:if (ftdi->bulk_in_left > 0) {
+ u8 c = ftdi->bulk_in_buffer[++ftdi->bulk_in_last];
+ bytes_read += 1;
+ ftdi->bulk_in_left -= 1;
+ if (ftdi->recieved == 0 && c == 0xFF) {
+ goto have;
+ } else
+ *b++ = c;
+ if (++ftdi->recieved < ftdi->expected) {
+ goto have;
+ } else if (ftdi->ed_found) {
+ int ed_number = (ftdi->response[0] >> 5) & 0x03;
+ u16 ed_length = (ftdi->response[2] << 8) |
+ ftdi->response[1];
+ struct u132_target *target = &ftdi->target[ed_number];
+ int payload = (ed_length >> 0) & 0x07FF;
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = payload;
+ u8 *c = 4 + ftdi->response;
+ int s = (sizeof(diag) - 1) / 3;
+ diag[0] = 0;
+ while (s-- > 0 && m-- > 0) {
+ if (s > 0 || m == 0) {
+ d += sprintf(d, " %02X", *c++);
+ } else
+ d += sprintf(d, " ..");
+ }
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ b = ftdi->response;
+ goto have;
+ } else if (ftdi->expected == 8) {
+ u8 buscmd;
+ int respond_head = ftdi->respond_head++;
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & respond_head];
+ u32 data = ftdi->response[7];
+ data <<= 8;
+ data |= ftdi->response[6];
+ data <<= 8;
+ data |= ftdi->response[5];
+ data <<= 8;
+ data |= ftdi->response[4];
+ *respond->value = data;
+ *respond->result = 0;
+ complete(&respond->wait_completion);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ b = ftdi->response;
+ buscmd = (ftdi->response[0] >> 0) & 0x0F;
+ if (buscmd == 0x00) {
+ } else if (buscmd == 0x02) {
+ } else if (buscmd == 0x06) {
+ } else if (buscmd == 0x0A) {
+ } else
+ dev_err(&ftdi->udev->dev, "Uxxx unknown(%0X) va"
+ "lue = %08X\n", buscmd, data);
+ goto have;
+ } else {
+ if ((ftdi->response[0] & 0x80) == 0x00) {
+ ftdi->expected = 8;
+ goto have;
+ } else {
+ int ed_number = (ftdi->response[0] >> 5) & 0x03;
+ int ed_type = (ftdi->response[0] >> 0) & 0x03;
+ u16 ed_length = (ftdi->response[2] << 8) |
+ ftdi->response[1];
+ struct u132_target *target = &ftdi->target[
+ ed_number];
+ target->halted = (ftdi->response[0] >> 3) &
+ 0x01;
+ target->skipped = (ftdi->response[0] >> 2) &
+ 0x01;
+ target->toggle_bits = (ftdi->response[3] >> 6)
+ & 0x03;
+ target->error_count = (ftdi->response[3] >> 4)
+ & 0x03;
+ target->condition_code = (ftdi->response[
+ 3] >> 0) & 0x0F;
+ if ((ftdi->response[0] & 0x10) == 0x00) {
+ b = have_ed_set_response(ftdi, target,
+ ed_length, ed_number, ed_type,
+ b);
+ goto have;
+ } else {
+ b = have_ed_get_response(ftdi, target,
+ ed_length, ed_number, ed_type,
+ b);
+ goto have;
+ }
+ }
+ }
+ } else
+ goto more;
+}
+
+
+/*
+* create a urb, and a buffer for it, and copy the data to the urb
+*
+*/
+static ssize_t ftdi_elan_write(struct file *file,
+ const char __user *user_buffer, size_t count,
+ loff_t *ppos)
+{
+ int retval = 0;
+ struct urb *urb;
+ char *buf;
+ char data[30 *3 + 4];
+ char *d = data;
+ const char __user *s = user_buffer;
+ int m = (sizeof(data) - 1) / 3;
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ }
+ if (count == 0) {
+ goto exit;
+ }
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ retval = -ENOMEM;
+ goto error_1;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, count, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ retval = -ENOMEM;
+ goto error_2;
+ }
+ if (copy_from_user(buf, user_buffer, count)) {
+ retval = -EFAULT;
+ goto error_3;
+ }
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, count,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed submitting write urb, error %"
+ "d\n", retval);
+ goto error_4;
+ }
+ usb_free_urb(urb);
+ exit:;
+ if (count > m) {
+ int I = m - 1;
+ while (I-- > 0) {
+ d += sprintf(d, " %02X", 0x000000FF & *s++);
+ }
+ d += sprintf(d, " ..");
+ } else {
+ int I = count;
+ while (I-- > 0) {
+ d += sprintf(d, " %02X", 0x000000FF & *s++);
+ }
+ }
+ return count;
+ error_4: error_3:usb_buffer_free(ftdi->udev, count, buf,
+ urb->transfer_dma);
+ error_2:usb_free_urb(urb);
+ error_1:return retval;
+}
+
+static struct file_operations ftdi_elan_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = ftdi_elan_ioctl,
+ .read = ftdi_elan_read,
+ .write = ftdi_elan_write,
+ .open = ftdi_elan_open,
+ .release = ftdi_elan_release,
+};
+
+/*
+* usb class driver info in order to get a minor number from the usb core,
+* and to have the device registered with the driver core
+*/
+static struct usb_class_driver ftdi_elan_jtag_class = {
+ .name = "ftdi-%d-jtag",
+ .fops = &ftdi_elan_fops,
+ .minor_base = USB_FTDI_ELAN_MINOR_BASE,
+};
+
+/*
+* the following definitions are for the
+* ELAN FPGA state machgine processor that
+* lies on the other side of the FTDI chip
+*/
+#define cPCIu132rd 0x0
+#define cPCIu132wr 0x1
+#define cPCIiord 0x2
+#define cPCIiowr 0x3
+#define cPCImemrd 0x6
+#define cPCImemwr 0x7
+#define cPCIcfgrd 0xA
+#define cPCIcfgwr 0xB
+#define cPCInull 0xF
+#define cU132cmd_status 0x0
+#define cU132flash 0x1
+#define cPIDsetup 0x0
+#define cPIDout 0x1
+#define cPIDin 0x2
+#define cPIDinonce 0x3
+#define cCCnoerror 0x0
+#define cCCcrc 0x1
+#define cCCbitstuff 0x2
+#define cCCtoggle 0x3
+#define cCCstall 0x4
+#define cCCnoresp 0x5
+#define cCCbadpid1 0x6
+#define cCCbadpid2 0x7
+#define cCCdataoverrun 0x8
+#define cCCdataunderrun 0x9
+#define cCCbuffoverrun 0xC
+#define cCCbuffunderrun 0xD
+#define cCCnotaccessed 0xF
+static int ftdi_elan_write_reg(struct usb_ftdi *ftdi, u32 data)
+{
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x00 | cPCIu132wr;
+ command->length = 0x04;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 4;
+ command->value = data;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+static int ftdi_elan_write_config(struct usb_ftdi *ftdi, int config_offset,
+ u8 width, u32 data)
+{
+ u8 addressofs = config_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x00 | (cPCIcfgwr & 0x0F);
+ command->length = 0x04;
+ command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 4;
+ command->value = data;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+static int ftdi_elan_write_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+ u8 width, u32 data)
+{
+ u8 addressofs = mem_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x00 | (cPCImemwr & 0x0F);
+ command->length = 0x04;
+ command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 4;
+ command->value = data;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
+ u8 width, u32 data)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_write_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_write_pcimem);
+static int ftdi_elan_read_reg(struct usb_ftdi *ftdi, u32 *data)
+{
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ int respond_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ respond_size = ftdi->respond_next - ftdi->respond_head;
+ if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+ {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & ftdi->respond_next];
+ int result = -ENODEV;
+ respond->result = &result;
+ respond->header = command->header = 0x00 | cPCIu132rd;
+ command->length = 0x04;
+ respond->address = command->address = cU132cmd_status;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ respond->value = data;
+ init_completion(&respond->wait_completion);
+ ftdi->command_next += 1;
+ ftdi->respond_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ wait_for_completion(&respond->wait_completion);
+ return result;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_read_reg(ftdi, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_reg);
+static int ftdi_elan_read_config(struct usb_ftdi *ftdi, int config_offset,
+ u8 width, u32 *data)
+{
+ u8 addressofs = config_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ int respond_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ respond_size = ftdi->respond_next - ftdi->respond_head;
+ if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+ {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & ftdi->respond_next];
+ int result = -ENODEV;
+ respond->result = &result;
+ respond->header = command->header = 0x00 | (cPCIcfgrd &
+ 0x0F);
+ command->length = 0x04;
+ respond->address = command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ respond->value = data;
+ init_completion(&respond->wait_completion);
+ ftdi->command_next += 1;
+ ftdi->respond_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ wait_for_completion(&respond->wait_completion);
+ return result;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+static int ftdi_elan_read_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+ u8 width, u32 *data)
+{
+ u8 addressofs = mem_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ int respond_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ respond_size = ftdi->respond_next - ftdi->respond_head;
+ if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+ {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & ftdi->respond_next];
+ int result = -ENODEV;
+ respond->result = &result;
+ respond->header = command->header = 0x00 | (cPCImemrd &
+ 0x0F);
+ command->length = 0x04;
+ respond->address = command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ respond->value = data;
+ init_completion(&respond->wait_completion);
+ ftdi->command_next += 1;
+ ftdi->respond_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ wait_for_completion(&respond->wait_completion);
+ return result;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
+ u8 width, u32 *data)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else
+ return ftdi_elan_read_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_pcimem);
+static int ftdi_elan_edset_setup(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x80 | (ed << 5);
+ command->length = 0x8007;
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 8;
+ command->value = 0;
+ command->buffer = urb->setup_packet;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_setup(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_setup);
+static int ftdi_elan_edset_input(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ int remaining_length = urb->transfer_buffer_length -
+ urb->actual_length;
+ command->header = 0x82 | (ed << 5);
+ if (remaining_length == 0) {
+ command->length = 0x0000;
+ } else if (remaining_length > 1024) {
+ command->length = 0x8000 | 1023;
+ } else
+ command->length = 0x8000 | (remaining_length -
+ 1);
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_input(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_input);
+static int ftdi_elan_edset_empty(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x81 | (ed << 5);
+ command->length = 0x0000;
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_empty(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_empty);
+static int ftdi_elan_edset_output(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ u8 *b;
+ u16 urb_size;
+ int i = 0;
+ char data[30 *3 + 4];
+ char *d = data;
+ int m = (sizeof(data) - 1) / 3;
+ int l = 0;
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x81 | (ed << 5);
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = min(1024,
+ urb->transfer_buffer_length -
+ urb->actual_length);
+ command->value = 0;
+ command->buffer = urb->transfer_buffer +
+ urb->actual_length;
+ command->length = 0x8000 | (command->follows - 1);
+ b = command->buffer;
+ urb_size = command->follows;
+ data[0] = 0;
+ while (urb_size-- > 0) {
+ if (i > m) {
+ } else if (i++ < m) {
+ int w = sprintf(d, " %02X", *b++);
+ d += w;
+ l += w;
+ } else
+ d += sprintf(d, " ..");
+ }
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_output(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_output);
+static int ftdi_elan_edset_single(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ int remaining_length = urb->transfer_buffer_length -
+ urb->actual_length;
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x83 | (ed << 5);
+ if (remaining_length == 0) {
+ command->length = 0x0000;
+ } else if (remaining_length > 1024) {
+ command->length = 0x8000 | 1023;
+ } else
+ command->length = 0x8000 | (remaining_length -
+ 1);
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_single(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_single);
+static int ftdi_elan_edset_flush(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp)
+{
+ u8 ed = ed_number - 1;
+ if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ struct u132_target *target = &ftdi->target[ed];
+ down(&ftdi->u132_lock);
+ if (target->abandoning > 0) {
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ target->abandoning = 1;
+ wait_1:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command =
+ &ftdi->command[COMMAND_MASK &
+ ftdi->command_next];
+ command->header = 0x80 | (ed << 5) |
+ 0x4;
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait_1;
+ }
+ }
+ up(&ftdi->u132_lock);
+ return 0;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+ void *endp)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_flush(ftdi, ed_number, endp);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_flush);
+static int ftdi_elan_flush_input_fifo(struct usb_ftdi *ftdi)
+{
+ int retry_on_empty = 10;
+ int retry_on_timeout = 5;
+ int retry_on_status = 20;
+ more:{
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(100));
+ if (packet_bytes > 2) {
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = (sizeof(diag) - 1) / 3;
+ char *b = ftdi->bulk_in_buffer;
+ int bytes_read = 0;
+ diag[0] = 0;
+ while (packet_bytes-- > 0) {
+ char c = *b++;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X",
+ 0x000000FF & c);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ bytes_read += 1;
+ continue;
+ }
+ goto more;
+ } else if (packet_bytes > 1) {
+ char s1 = ftdi->bulk_in_buffer[0];
+ char s2 = ftdi->bulk_in_buffer[1];
+ if (s1 == 0x31 && s2 == 0x60) {
+ return 0;
+ } else if (retry_on_status-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+ "imit reached\n");
+ return -EFAULT;
+ }
+ } else if (packet_bytes > 0) {
+ char b1 = ftdi->bulk_in_buffer[0];
+ dev_err(&ftdi->udev->dev, "only one byte flushed from F"
+ "TDI = %02X\n", b1);
+ if (retry_on_status-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+ "imit reached\n");
+ return -EFAULT;
+ }
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT retry limi"
+ "t reached\n");
+ return -ENOMEM;
+ }
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "empty packet retry l"
+ "imit reached\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+ return retval;
+ }
+ }
+ return -1;
+}
+
+
+/*
+* send the long flush sequence
+*
+*/
+static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi)
+{
+ int retval;
+ struct urb *urb;
+ char *buf;
+ int I = 257;
+ int i = 0;
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&ftdi->udev->dev, "could not alloc a urb for flush sequ"
+ "ence\n");
+ return -ENOMEM;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ dev_err(&ftdi->udev->dev, "could not get a buffer for flush seq"
+ "uence\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ while (I-- > 0)
+ buf[i++] = 0x55;
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, i,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
+ "flush sequence\n");
+ usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ usb_free_urb(urb);
+ return 0;
+}
+
+
+/*
+* send the reset sequence
+*
+*/
+static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi)
+{
+ int retval;
+ struct urb *urb;
+ char *buf;
+ int I = 4;
+ int i = 0;
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&ftdi->udev->dev, "could not get a urb for the reset se"
+ "quence\n");
+ return -ENOMEM;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ dev_err(&ftdi->udev->dev, "could not get a buffer for the reset"
+ " sequence\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ buf[i++] = 0x55;
+ buf[i++] = 0xAA;
+ buf[i++] = 0x5A;
+ buf[i++] = 0xA5;
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, i,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
+ "reset sequence\n");
+ usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ usb_free_urb(urb);
+ return 0;
+}
+
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi)
+{
+ int retval;
+ int long_stop = 10;
+ int retry_on_timeout = 5;
+ int retry_on_empty = 10;
+ int err_count = 0;
+ retval = ftdi_elan_flush_input_fifo(ftdi);
+ if (retval)
+ return retval;
+ ftdi->bulk_in_left = 0;
+ ftdi->bulk_in_last = -1;
+ while (long_stop-- > 0) {
+ int read_stop;
+ int read_stuck;
+ retval = ftdi_elan_synchronize_flush(ftdi);
+ if (retval)
+ return retval;
+ retval = ftdi_elan_flush_input_fifo(ftdi);
+ if (retval)
+ return retval;
+ reset:retval = ftdi_elan_synchronize_reset(ftdi);
+ if (retval)
+ return retval;
+ read_stop = 100;
+ read_stuck = 10;
+ read:{
+ int packet_bytes = 0;
+ retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev,
+ ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(500));
+ if (packet_bytes > 2) {
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = (sizeof(diag) - 1) / 3;
+ char *b = ftdi->bulk_in_buffer;
+ int bytes_read = 0;
+ unsigned char c = 0;
+ diag[0] = 0;
+ while (packet_bytes-- > 0) {
+ c = *b++;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X", c);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ bytes_read += 1;
+ continue;
+ }
+ if (c == 0x7E) {
+ return 0;
+ } else {
+ if (c == 0x55) {
+ goto read;
+ } else if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retr"
+ "y limit reached\n");
+ continue;
+ }
+ }
+ } else if (packet_bytes > 1) {
+ unsigned char s1 = ftdi->bulk_in_buffer[0];
+ unsigned char s2 = ftdi->bulk_in_buffer[1];
+ if (s1 == 0x31 && s2 == 0x00) {
+ if (read_stuck-- > 0) {
+ goto read;
+ } else
+ goto reset;
+ } else if (s1 == 0x31 && s2 == 0x60) {
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retr"
+ "y limit reached\n");
+ continue;
+ }
+ } else {
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retr"
+ "y limit reached\n");
+ continue;
+ }
+ }
+ } else if (packet_bytes > 0) {
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retry limit "
+ "reached\n");
+ continue;
+ }
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT re"
+ "try limit reached\n");
+ continue;
+ }
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "empty packet"
+ " retry limit reached\n");
+ continue;
+ }
+ } else {
+ err_count += 1;
+ dev_err(&ftdi->udev->dev, "error = %d\n",
+ retval);
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retry limit "
+ "reached\n");
+ continue;
+ }
+ }
+ }
+ }
+ dev_err(&ftdi->udev->dev, "failed to synchronize\n");
+ return -EFAULT;
+}
+
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi)
+{
+ int retry_on_empty = 10;
+ int retry_on_timeout = 5;
+ int retry_on_status = 50;
+ more:{
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(1000));
+ if (packet_bytes > 2) {
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = (sizeof(diag) - 1) / 3;
+ char *b = ftdi->bulk_in_buffer;
+ int bytes_read = 0;
+ diag[0] = 0;
+ while (packet_bytes-- > 0) {
+ char c = *b++;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X",
+ 0x000000FF & c);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ bytes_read += 1;
+ continue;
+ }
+ goto more;
+ } else if (packet_bytes > 1) {
+ char s1 = ftdi->bulk_in_buffer[0];
+ char s2 = ftdi->bulk_in_buffer[1];
+ if (s1 == 0x31 && s2 == 0x60) {
+ return 0;
+ } else if (retry_on_status-- > 0) {
+ msleep(5);
+ goto more;
+ } else
+ return -EFAULT;
+ } else if (packet_bytes > 0) {
+ char b1 = ftdi->bulk_in_buffer[0];
+ dev_err(&ftdi->udev->dev, "only one byte flushed from F"
+ "TDI = %02X\n", b1);
+ if (retry_on_status-- > 0) {
+ msleep(5);
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+ "imit reached\n");
+ return -EFAULT;
+ }
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT retry limi"
+ "t reached\n");
+ return -ENOMEM;
+ }
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "empty packet retry l"
+ "imit reached\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+ return -ENOMEM;
+ }
+ }
+ return -1;
+}
+
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
+{
+ int UxxxStatus = ftdi_elan_read_reg(ftdi, &ftdi->controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ if (ftdi->controlreg & 0x00400000) {
+ if (ftdi->card_ejected) {
+ } else {
+ ftdi->card_ejected = 1;
+ dev_err(&ftdi->udev->dev, "CARD EJECTED - controlreg = "
+ "%08X\n", ftdi->controlreg);
+ }
+ return -ENODEV;
+ } else {
+ u8 fn = ftdi->function - 1;
+ int activePCIfn = fn << 8;
+ u32 pcidata;
+ u32 pciVID;
+ u32 pciPID;
+ int reg = 0;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ pciVID = pcidata & 0xFFFF;
+ pciPID = (pcidata >> 16) & 0xFFFF;
+ if (pciVID == ftdi->platform_data.vendor && pciPID ==
+ ftdi->platform_data.device) {
+ return 0;
+ } else {
+ dev_err(&ftdi->udev->dev, "vendor=%04X pciVID=%04X devi"
+ "ce=%04X pciPID=%04X\n",
+ ftdi->platform_data.vendor, pciVID,
+ ftdi->platform_data.device, pciPID);
+ return -ENODEV;
+ }
+ }
+}
+
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
+{
+ u32 latence_timer;
+ u32 controlreg;
+ int UxxxStatus;
+ u32 pcidata;
+ int reg = 0;
+ int foundOHCI = 0;
+ u8 fn;
+ int activePCIfn = 0;
+ u32 pciVID = 0;
+ u32 pciPID = 0;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000000L);
+ if (UxxxStatus)
+ return UxxxStatus;
+ msleep(750);
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x100);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x500);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020CL | 0x000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020DL | 0x000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ msleep(250);
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020FL | 0x000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x800);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ msleep(1000);
+ for (fn = 0; (fn < 4) && (!foundOHCI); fn++) {
+ activePCIfn = fn << 8;
+ ftdi->function = fn + 1;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ pciVID = pcidata & 0xFFFF;
+ pciPID = (pcidata >> 16) & 0xFFFF;
+ if ((pciVID == 0x1045) && (pciPID == 0xc861)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x1033) && (pciPID == 0x0035)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x10b9) && (pciPID == 0x5237)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x11c1) && (pciPID == 0x5802)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x11AB) && (pciPID == 0x1FA6)) {
+ }
+ }
+ if (foundOHCI == 0) {
+ return -ENXIO;
+ }
+ ftdi->platform_data.vendor = pciVID;
+ ftdi->platform_data.device = pciPID;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800);
+ if (UxxxStatus)
+ return UxxxStatus;
+ reg = 16;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+ 0xFFFFFFFF);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+ 0xF0000000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ reg = 12;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &latence_timer);
+ if (UxxxStatus)
+ return UxxxStatus;
+ latence_timer &= 0xFFFF00FF;
+ latence_timer |= 0x00001600;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+ latence_timer);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ reg = 4;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+ 0x06);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ return 0;
+}
+
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi)
+{
+ u32 pcidata;
+ int U132Status;
+ int reg;
+ int reset_repeat = 0;
+ do_reset:reg = 8;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x01);
+ if (U132Status)
+ return U132Status;
+ reset_check:{
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ if (pcidata & 1) {
+ msleep(500);
+ if (reset_repeat++ > 100) {
+ reset_repeat = 0;
+ goto do_reset;
+ } else
+ goto reset_check;
+ }
+ }
+ goto dump_regs;
+ msleep(500);
+ reg = 0x28;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x11000000);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x40;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x34;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf2edf);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 4;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0xA0);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ msleep(250);
+ reg = 8;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x04);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x28;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 8;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x48;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x00001200);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x58;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x34;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x28002edf);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ msleep(100);
+ reg = 0x50;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10000);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ power_check:U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ if (!(pcidata & 1)) {
+ msleep(500);
+ goto power_check;
+ }
+ msleep(3000);
+ reg = 0x54;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x58;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ msleep(750);
+ reg = 0x54;
+ if (0) {
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
+ if (U132Status)
+ return U132Status;
+ }
+ if (0) {
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ }
+ reg = 0x54;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x58;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ dump_regs:for (reg = 0; reg <= 0x54; reg += 4) {
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ }
+ return 0;
+}
+
+
+/*
+* we use only the first bulk-in and bulk-out endpoints
+*/
+static int ftdi_elan_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ size_t buffer_size;
+ int i;
+ int retval = -ENOMEM;
+ struct usb_ftdi *ftdi = kmalloc(sizeof(struct usb_ftdi), GFP_KERNEL);
+ if (ftdi == NULL) {
+ printk(KERN_ERR "Out of memory\n");
+ return -ENOMEM;
+ }
+ memset(ftdi, 0x00, sizeof(struct usb_ftdi));
+ down(&ftdi_module_lock);
+ list_add_tail(&ftdi->ftdi_list, &ftdi_static_list);
+ ftdi->sequence_num = ++ftdi_instances;
+ up(&ftdi_module_lock);
+ ftdi_elan_init_kref(ftdi);
+ init_MUTEX(&ftdi->sw_lock);
+ ftdi->udev = usb_get_dev(interface_to_usbdev(interface));
+ ftdi->interface = interface;
+ init_MUTEX(&ftdi->u132_lock);
+ ftdi->expected = 4;
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!ftdi->bulk_in_endpointAddr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) && ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
+ {
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ ftdi->bulk_in_size = buffer_size;
+ ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+ ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!ftdi->bulk_in_buffer) {
+ dev_err(&ftdi->udev->dev, "Could not allocate b"
+ "ulk_in_buffer\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+ }
+ if (!ftdi->bulk_out_endpointAddr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_OUT) && ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
+ {
+ ftdi->bulk_out_endpointAddr =
+ endpoint->bEndpointAddress;
+ }
+ }
+ if (!(ftdi->bulk_in_endpointAddr && ftdi->bulk_out_endpointAddr)) {
+ dev_err(&ftdi->udev->dev, "Could not find both bulk-in and bulk"
+ "-out endpoints\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ dev_info(&ftdi->udev->dev, "interface %d has I=%02X O=%02X\n",
+ iface_desc->desc.bInterfaceNumber, ftdi->bulk_in_endpointAddr,
+ ftdi->bulk_out_endpointAddr);
+ usb_set_intfdata(interface, ftdi);
+ if (iface_desc->desc.bInterfaceNumber == 0 &&
+ ftdi->bulk_in_endpointAddr == 0x81 &&
+ ftdi->bulk_out_endpointAddr == 0x02) {
+ retval = usb_register_dev(interface, &ftdi_elan_jtag_class);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "Not able to get a minor for "
+ "this device.\n");
+ usb_set_intfdata(interface, NULL);
+ retval = -ENOMEM;
+ goto error;
+ } else {
+ ftdi->class = &ftdi_elan_jtag_class;
+ dev_info(&ftdi->udev->dev, "USB FDTI=%p JTAG interface "
+ "%d now attached to ftdi%d\n", ftdi,
+ iface_desc->desc.bInterfaceNumber,
+ interface->minor);
+ return 0;
+ }
+ } else if (iface_desc->desc.bInterfaceNumber == 1 &&
+ ftdi->bulk_in_endpointAddr == 0x83 &&
+ ftdi->bulk_out_endpointAddr == 0x04) {
+ ftdi->class = NULL;
+ dev_info(&ftdi->udev->dev, "USB FDTI=%p ELAN interface %d now a"
+ "ctivated\n", ftdi, iface_desc->desc.bInterfaceNumber);
+ INIT_WORK(&ftdi->status_work, ftdi_elan_status_work,
+ (void *)ftdi);
+ INIT_WORK(&ftdi->command_work, ftdi_elan_command_work,
+ (void *)ftdi);
+ INIT_WORK(&ftdi->respond_work, ftdi_elan_respond_work,
+ (void *)ftdi);
+ ftdi_status_queue_work(ftdi, msecs_to_jiffies(3 *1000));
+ return 0;
+ } else {
+ dev_err(&ftdi->udev->dev,
+ "Could not find ELAN's U132 device\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ error:if (ftdi) {
+ ftdi_elan_put_kref(ftdi);
+ }
+ return retval;
+}
+
+static void ftdi_elan_disconnect(struct usb_interface *interface)
+{
+ struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+ ftdi->disconnected += 1;
+ if (ftdi->class) {
+ int minor = interface->minor;
+ struct usb_class_driver *class = ftdi->class;
+ usb_set_intfdata(interface, NULL);
+ usb_deregister_dev(interface, class);
+ dev_info(&ftdi->udev->dev, "USB FTDI U132 jtag interface on min"
+ "or %d now disconnected\n", minor);
+ } else {
+ ftdi_status_cancel_work(ftdi);
+ ftdi_command_cancel_work(ftdi);
+ ftdi_response_cancel_work(ftdi);
+ ftdi_elan_abandon_completions(ftdi);
+ ftdi_elan_abandon_targets(ftdi);
+ if (ftdi->registered) {
+ platform_device_unregister(&ftdi->platform_dev);
+ ftdi->synchronized = 0;
+ ftdi->enumerated = 0;
+ ftdi->registered = 0;
+ }
+ flush_workqueue(status_queue);
+ flush_workqueue(command_queue);
+ flush_workqueue(respond_queue);
+ ftdi->disconnected += 1;
+ usb_set_intfdata(interface, NULL);
+ dev_info(&ftdi->udev->dev, "USB FTDI U132 host controller inter"
+ "face now disconnected\n");
+ }
+ ftdi_elan_put_kref(ftdi);
+}
+
+static struct usb_driver ftdi_elan_driver = {
+ .name = "ftdi-elan",
+ .probe = ftdi_elan_probe,
+ .disconnect = ftdi_elan_disconnect,
+ .id_table = ftdi_elan_table,
+};
+static int __init ftdi_elan_init(void)
+{
+ int result;
+ printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name,
+ __TIME__, __DATE__);
+ init_MUTEX(&ftdi_module_lock);
+ INIT_LIST_HEAD(&ftdi_static_list);
+ status_queue = create_singlethread_workqueue("ftdi-status-control");
+ command_queue = create_singlethread_workqueue("ftdi-command-engine");
+ respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
+ result = usb_register(&ftdi_elan_driver);
+ if (result)
+ printk(KERN_ERR "usb_register failed. Error number %d\n",
+ result);
+ return result;
+}
+
+static void __exit ftdi_elan_exit(void)
+{
+ struct usb_ftdi *ftdi;
+ struct usb_ftdi *temp;
+ usb_deregister(&ftdi_elan_driver);
+ printk(KERN_INFO "ftdi_u132 driver deregistered\n");
+ list_for_each_entry_safe(ftdi, temp, &ftdi_static_list, ftdi_list) {
+ ftdi_status_cancel_work(ftdi);
+ ftdi_command_cancel_work(ftdi);
+ ftdi_response_cancel_work(ftdi);
+ } flush_workqueue(status_queue);
+ destroy_workqueue(status_queue);
+ status_queue = NULL;
+ flush_workqueue(command_queue);
+ destroy_workqueue(command_queue);
+ command_queue = NULL;
+ flush_workqueue(respond_queue);
+ destroy_workqueue(respond_queue);
+ respond_queue = NULL;
+}
+
+
+module_init(ftdi_elan_init);
+module_exit(ftdi_elan_exit);
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index fcd69c52aea..8e6e195a22b 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -98,7 +98,7 @@ static int idmouse_probe(struct usb_interface *interface,
static void idmouse_disconnect(struct usb_interface *interface);
/* file operation pointers */
-static struct file_operations idmouse_fops = {
+static const struct file_operations idmouse_fops = {
.owner = THIS_MODULE,
.read = idmouse_read,
.open = idmouse_open,
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index f30ab1fbb3c..10b640339d8 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -589,7 +589,7 @@ exit:
}
/* file operations needed when we register this driver */
-static struct file_operations ld_usb_fops = {
+static const struct file_operations ld_usb_fops = {
.owner = THIS_MODULE,
.read = ld_usb_read,
.write = ld_usb_write,
@@ -657,15 +657,11 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ if (usb_endpoint_is_int_in(endpoint))
dev->interrupt_in_endpoint = endpoint;
- }
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ if (usb_endpoint_is_int_out(endpoint))
dev->interrupt_out_endpoint = endpoint;
- }
}
if (dev->interrupt_in_endpoint == NULL) {
dev_err(&intf->dev, "Interrupt in endpoint not found\n");
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 7699d970e68..77c36e63c7b 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -259,7 +259,7 @@ static void tower_disconnect (struct usb_interface *interface);
static DEFINE_MUTEX (disconnect_mutex);
/* file operations needed when we register this driver */
-static struct file_operations tower_fops = {
+static const struct file_operations tower_fops = {
.owner = THIS_MODULE,
.read = tower_read,
.write = tower_write,
diff --git a/drivers/usb/misc/phidget.c b/drivers/usb/misc/phidget.c
new file mode 100644
index 00000000000..735ed33f4f7
--- /dev/null
+++ b/drivers/usb/misc/phidget.c
@@ -0,0 +1,43 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+struct class *phidget_class;
+
+static int __init init_phidget(void)
+{
+ phidget_class = class_create(THIS_MODULE, "phidget");
+
+ if (IS_ERR(phidget_class))
+ return PTR_ERR(phidget_class);
+
+ return 0;
+}
+
+static void __exit cleanup_phidget(void)
+{
+ class_destroy(phidget_class);
+}
+
+EXPORT_SYMBOL_GPL(phidget_class);
+
+module_init(init_phidget);
+module_exit(cleanup_phidget);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("Container module for phidget class");
+
diff --git a/drivers/usb/misc/phidget.h b/drivers/usb/misc/phidget.h
new file mode 100644
index 00000000000..c4011907d43
--- /dev/null
+++ b/drivers/usb/misc/phidget.h
@@ -0,0 +1,12 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+extern struct class *phidget_class;
diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c
index bfbbbfbb92b..78e419904ab 100644
--- a/drivers/usb/misc/phidgetkit.c
+++ b/drivers/usb/misc/phidgetkit.c
@@ -20,6 +20,8 @@
#include <linux/module.h>
#include <linux/usb.h>
+#include "phidget.h"
+
#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
#define DRIVER_DESC "USB PhidgetInterfaceKit Driver"
@@ -42,26 +44,35 @@ struct driver_interfacekit {
int inputs;
int outputs;
int has_lcd;
+ int amnesiac;
};
-#define ifkit(_sensors, _inputs, _outputs, _lcd) \
-static struct driver_interfacekit ph_##_sensors##_inputs##_outputs = { \
+
+#define ifkit(_sensors, _inputs, _outputs, _lcd, _amnesiac) \
+{ \
.sensors = _sensors, \
.inputs = _inputs, \
.outputs = _outputs, \
.has_lcd = _lcd, \
+ .amnesiac = _amnesiac \
};
-ifkit(0, 0, 4, 0);
-ifkit(8, 8, 8, 0);
-ifkit(0, 4, 7, 1);
-ifkit(8, 8, 4, 0);
-ifkit(0, 8, 8, 1);
-ifkit(0, 16, 16, 0);
+
+static const struct driver_interfacekit ph_004 = ifkit(0, 0, 4, 0, 0);
+static const struct driver_interfacekit ph_888n = ifkit(8, 8, 8, 0, 1);
+static const struct driver_interfacekit ph_888o = ifkit(8, 8, 8, 0, 0);
+static const struct driver_interfacekit ph_047 = ifkit(0, 4, 7, 1, 0);
+static const struct driver_interfacekit ph_884 = ifkit(8, 8, 4, 0, 0);
+static const struct driver_interfacekit ph_088 = ifkit(0, 8, 8, 1, 0);
+static const struct driver_interfacekit ph_01616 = ifkit(0, 16, 16, 0, 0);
+
+static unsigned long device_no;
struct interfacekit {
struct usb_device *udev;
struct usb_interface *intf;
struct driver_interfacekit *ifkit;
+ struct device *dev;
unsigned long outputs;
+ int dev_no;
u8 inputs[MAX_INTERFACES];
u16 sensors[MAX_INTERFACES];
u8 lcd_files_on;
@@ -71,6 +82,7 @@ struct interfacekit {
dma_addr_t data_dma;
struct work_struct do_notify;
+ struct work_struct do_resubmit;
unsigned long input_events;
unsigned long sensor_events;
};
@@ -78,8 +90,10 @@ struct interfacekit {
static struct usb_device_id id_table[] = {
{USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT004),
.driver_info = (kernel_ulong_t)&ph_004},
- {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888),
- .driver_info = (kernel_ulong_t)&ph_888},
+ {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0, 0x814),
+ .driver_info = (kernel_ulong_t)&ph_888o},
+ {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0x0815, 0xffff),
+ .driver_info = (kernel_ulong_t)&ph_888n},
{USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT047),
.driver_info = (kernel_ulong_t)&ph_047},
{USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT088),
@@ -92,16 +106,11 @@ static struct usb_device_id id_table[] = {
};
MODULE_DEVICE_TABLE(usb, id_table);
-static int change_outputs(struct interfacekit *kit, int output_num, int enable)
+static int set_outputs(struct interfacekit *kit)
{
u8 *buffer;
int retval;
- if (enable)
- set_bit(output_num, &kit->outputs);
- else
- clear_bit(output_num, &kit->outputs);
-
buffer = kzalloc(4, GFP_KERNEL);
if (!buffer) {
dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__);
@@ -121,6 +130,9 @@ static int change_outputs(struct interfacekit *kit, int output_num, int enable)
retval);
kfree(buffer);
+ if (kit->ifkit->amnesiac)
+ schedule_delayed_work(&kit->do_resubmit, HZ / 2);
+
return retval < 0 ? retval : 0;
}
@@ -180,21 +192,24 @@ exit:
}
#define set_lcd_line(number) \
-static ssize_t lcd_line_##number(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
-{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
- change_string(kit, buf, number - 1); \
- return count; \
-} \
-static DEVICE_ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number);
+static ssize_t lcd_line_##number(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ change_string(kit, buf, number - 1); \
+ return count; \
+}
+
+#define lcd_line_attr(number) \
+ __ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number)
+
set_lcd_line(1);
set_lcd_line(2);
static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
- struct usb_interface *intf = to_usb_interface(dev);
- struct interfacekit *kit = usb_get_intfdata(intf);
+ struct interfacekit *kit = dev_get_drvdata(dev);
int enabled;
unsigned char *buffer;
int retval = -ENOMEM;
@@ -226,23 +241,30 @@ exit:
kfree(buffer);
return retval;
}
-static DEVICE_ATTR(backlight, S_IWUGO, NULL, set_backlight);
+
+static struct device_attribute dev_lcd_line_attrs[] = {
+ lcd_line_attr(1),
+ lcd_line_attr(2),
+ __ATTR(backlight, S_IWUGO, NULL, set_backlight)
+};
static void remove_lcd_files(struct interfacekit *kit)
{
+ int i;
+
if (kit->lcd_files_on) {
dev_dbg(&kit->udev->dev, "Removing lcd files\n");
- device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_1);
- device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_2);
- device_remove_file(&kit->intf->dev, &dev_attr_backlight);
+
+ for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++)
+ device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
}
}
static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
- struct usb_interface *intf = to_usb_interface(dev);
- struct interfacekit *kit = usb_get_intfdata(intf);
+ struct interfacekit *kit = dev_get_drvdata(dev);
int enable;
+ int i, rc;
if (kit->ifkit->has_lcd == 0)
return -ENODEV;
@@ -253,9 +275,12 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att
if (enable) {
if (!kit->lcd_files_on) {
dev_dbg(&kit->udev->dev, "Adding lcd files\n");
- device_create_file(&kit->intf->dev, &dev_attr_lcd_line_1);
- device_create_file(&kit->intf->dev, &dev_attr_lcd_line_2);
- device_create_file(&kit->intf->dev, &dev_attr_backlight);
+ for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++) {
+ rc = device_create_file(kit->dev,
+ &dev_lcd_line_attrs[i]);
+ if (rc)
+ goto out;
+ }
kit->lcd_files_on = 1;
}
} else {
@@ -266,7 +291,13 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att
}
return count;
+out:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
+
+ return rc;
}
+
static DEVICE_ATTR(lcd, S_IWUGO, NULL, enable_lcd_files);
static void interfacekit_irq(struct urb *urb, struct pt_regs *regs)
@@ -362,44 +393,58 @@ static void do_notify(void *data)
for (i=0; i<kit->ifkit->inputs; i++) {
if (test_and_clear_bit(i, &kit->input_events)) {
sprintf(sysfs_file, "input%d", i + 1);
- sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+ sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
}
}
for (i=0; i<kit->ifkit->sensors; i++) {
if (test_and_clear_bit(i, &kit->sensor_events)) {
sprintf(sysfs_file, "sensor%d", i + 1);
- sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+ sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
}
}
}
+static void do_resubmit(void *data)
+{
+ set_outputs(data);
+}
+
#define show_set_output(value) \
-static ssize_t set_output##value(struct device *dev, struct device_attribute *attr, const char *buf, \
- size_t count) \
+static ssize_t set_output##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
- int enabled; \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ int enable; \
int retval; \
\
- if (sscanf(buf, "%d", &enabled) < 1) \
+ if (sscanf(buf, "%d", &enable) < 1) \
return -EINVAL; \
\
- retval = change_outputs(kit, value - 1, enabled); \
+ if (enable) \
+ set_bit(value - 1, &kit->outputs); \
+ else \
+ clear_bit(value - 1, &kit->outputs); \
+ \
+ retval = set_outputs(kit); \
\
return retval ? retval : count; \
} \
\
-static ssize_t show_output##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_output##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
\
return sprintf(buf, "%d\n", !!test_bit(value - 1, &kit->outputs));\
-} \
-static DEVICE_ATTR(output##value, S_IWUGO | S_IRUGO, \
- show_output##value, set_output##value);
+}
+
+#define output_attr(value) \
+ __ATTR(output##value, S_IWUGO | S_IRUGO, \
+ show_output##value, set_output##value)
+
show_set_output(1);
show_set_output(2);
show_set_output(3);
@@ -417,15 +462,24 @@ show_set_output(14);
show_set_output(15);
show_set_output(16);
+static struct device_attribute dev_output_attrs[] = {
+ output_attr(1), output_attr(2), output_attr(3), output_attr(4),
+ output_attr(5), output_attr(6), output_attr(7), output_attr(8),
+ output_attr(9), output_attr(10), output_attr(11), output_attr(12),
+ output_attr(13), output_attr(14), output_attr(15), output_attr(16)
+};
+
#define show_input(value) \
-static ssize_t show_input##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_input##value(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
\
return sprintf(buf, "%d\n", (int)kit->inputs[value - 1]); \
-} \
-static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL);
+}
+
+#define input_attr(value) \
+ __ATTR(input##value, S_IRUGO, show_input##value, NULL)
show_input(1);
show_input(2);
@@ -444,15 +498,25 @@ show_input(14);
show_input(15);
show_input(16);
+static struct device_attribute dev_input_attrs[] = {
+ input_attr(1), input_attr(2), input_attr(3), input_attr(4),
+ input_attr(5), input_attr(6), input_attr(7), input_attr(8),
+ input_attr(9), input_attr(10), input_attr(11), input_attr(12),
+ input_attr(13), input_attr(14), input_attr(15), input_attr(16)
+};
+
#define show_sensor(value) \
-static ssize_t show_sensor##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_sensor##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
\
return sprintf(buf, "%d\n", (int)kit->sensors[value - 1]); \
-} \
-static DEVICE_ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL);
+}
+
+#define sensor_attr(value) \
+ __ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL)
show_sensor(1);
show_sensor(2);
@@ -463,6 +527,11 @@ show_sensor(6);
show_sensor(7);
show_sensor(8);
+static struct device_attribute dev_sensor_attrs[] = {
+ sensor_attr(1), sensor_attr(2), sensor_attr(3), sensor_attr(4),
+ sensor_attr(5), sensor_attr(6), sensor_attr(7), sensor_attr(8)
+};
+
static int interfacekit_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
@@ -471,6 +540,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
struct interfacekit *kit;
struct driver_interfacekit *ifkit;
int pipe, maxp, rc = -ENOMEM;
+ int bit, value, i;
ifkit = (struct driver_interfacekit *)id->driver_info;
if (!ifkit)
@@ -493,6 +563,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
if (!kit)
goto out;
+ kit->dev_no = -1;
kit->ifkit = ifkit;
kit->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &kit->data_dma);
if (!kit->data)
@@ -505,6 +576,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
kit->udev = usb_get_dev(dev);
kit->intf = intf;
INIT_WORK(&kit->do_notify, do_notify, kit);
+ INIT_WORK(&kit->do_resubmit, do_resubmit, kit);
usb_fill_int_urb(kit->irq, kit->udev, pipe, kit->data,
maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
interfacekit_irq, kit, endpoint->bInterval);
@@ -513,85 +585,80 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
usb_set_intfdata(intf, kit);
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while(value);
+ kit->dev_no = bit;
+
+ kit->dev = device_create(phidget_class, &kit->udev->dev, 0,
+ "interfacekit%d", kit->dev_no);
+ if (IS_ERR(kit->dev)) {
+ rc = PTR_ERR(kit->dev);
+ kit->dev = NULL;
+ goto out;
+ }
+ dev_set_drvdata(kit->dev, kit);
+
if (usb_submit_urb(kit->irq, GFP_KERNEL)) {
rc = -EIO;
goto out;
}
- if (ifkit->outputs >= 4) {
- device_create_file(&intf->dev, &dev_attr_output1);
- device_create_file(&intf->dev, &dev_attr_output2);
- device_create_file(&intf->dev, &dev_attr_output3);
- device_create_file(&intf->dev, &dev_attr_output4);
- }
- if (ifkit->outputs >= 8) {
- device_create_file(&intf->dev, &dev_attr_output5);
- device_create_file(&intf->dev, &dev_attr_output6);
- device_create_file(&intf->dev, &dev_attr_output7);
- device_create_file(&intf->dev, &dev_attr_output8);
- }
- if (ifkit->outputs == 16) {
- device_create_file(&intf->dev, &dev_attr_output9);
- device_create_file(&intf->dev, &dev_attr_output10);
- device_create_file(&intf->dev, &dev_attr_output11);
- device_create_file(&intf->dev, &dev_attr_output12);
- device_create_file(&intf->dev, &dev_attr_output13);
- device_create_file(&intf->dev, &dev_attr_output14);
- device_create_file(&intf->dev, &dev_attr_output15);
- device_create_file(&intf->dev, &dev_attr_output16);
+ for (i=0; i<ifkit->outputs; i++ ) {
+ rc = device_create_file(kit->dev, &dev_output_attrs[i]);
+ if (rc)
+ goto out2;
}
- if (ifkit->inputs >= 4) {
- device_create_file(&intf->dev, &dev_attr_input1);
- device_create_file(&intf->dev, &dev_attr_input2);
- device_create_file(&intf->dev, &dev_attr_input3);
- device_create_file(&intf->dev, &dev_attr_input4);
- }
- if (ifkit->inputs >= 8) {
- device_create_file(&intf->dev, &dev_attr_input5);
- device_create_file(&intf->dev, &dev_attr_input6);
- device_create_file(&intf->dev, &dev_attr_input7);
- device_create_file(&intf->dev, &dev_attr_input8);
- }
- if (ifkit->inputs == 16) {
- device_create_file(&intf->dev, &dev_attr_input9);
- device_create_file(&intf->dev, &dev_attr_input10);
- device_create_file(&intf->dev, &dev_attr_input11);
- device_create_file(&intf->dev, &dev_attr_input12);
- device_create_file(&intf->dev, &dev_attr_input13);
- device_create_file(&intf->dev, &dev_attr_input14);
- device_create_file(&intf->dev, &dev_attr_input15);
- device_create_file(&intf->dev, &dev_attr_input16);
+ for (i=0; i<ifkit->inputs; i++ ) {
+ rc = device_create_file(kit->dev, &dev_input_attrs[i]);
+ if (rc)
+ goto out3;
}
- if (ifkit->sensors >= 4) {
- device_create_file(&intf->dev, &dev_attr_sensor1);
- device_create_file(&intf->dev, &dev_attr_sensor2);
- device_create_file(&intf->dev, &dev_attr_sensor3);
- device_create_file(&intf->dev, &dev_attr_sensor4);
+ for (i=0; i<ifkit->sensors; i++ ) {
+ rc = device_create_file(kit->dev, &dev_sensor_attrs[i]);
+ if (rc)
+ goto out4;
}
- if (ifkit->sensors >= 7) {
- device_create_file(&intf->dev, &dev_attr_sensor5);
- device_create_file(&intf->dev, &dev_attr_sensor6);
- device_create_file(&intf->dev, &dev_attr_sensor7);
- }
- if (ifkit->sensors == 8)
- device_create_file(&intf->dev, &dev_attr_sensor8);
- if (ifkit->has_lcd)
- device_create_file(&intf->dev, &dev_attr_lcd);
+ if (ifkit->has_lcd) {
+ rc = device_create_file(kit->dev, &dev_attr_lcd);
+ if (rc)
+ goto out4;
+
+ }
dev_info(&intf->dev, "USB PhidgetInterfaceKit %d/%d/%d attached\n",
ifkit->sensors, ifkit->inputs, ifkit->outputs);
return 0;
+out4:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+ i = ifkit->inputs;
+out3:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_input_attrs[i]);
+
+ i = ifkit->outputs;
+out2:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_output_attrs[i]);
out:
if (kit) {
if (kit->irq)
usb_free_urb(kit->irq);
if (kit->data)
usb_buffer_free(dev, URB_INT_SIZE, kit->data, kit->data_dma);
+ if (kit->dev)
+ device_unregister(kit->dev);
+ if (kit->dev_no >= 0)
+ clear_bit(kit->dev_no, &device_no);
+
kfree(kit);
}
@@ -601,6 +668,7 @@ out:
static void interfacekit_disconnect(struct usb_interface *interface)
{
struct interfacekit *kit;
+ int i;
kit = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
@@ -612,74 +680,30 @@ static void interfacekit_disconnect(struct usb_interface *interface)
usb_buffer_free(kit->udev, URB_INT_SIZE, kit->data, kit->data_dma);
cancel_delayed_work(&kit->do_notify);
+ cancel_delayed_work(&kit->do_resubmit);
- if (kit->ifkit->outputs >= 4) {
- device_remove_file(&interface->dev, &dev_attr_output1);
- device_remove_file(&interface->dev, &dev_attr_output2);
- device_remove_file(&interface->dev, &dev_attr_output3);
- device_remove_file(&interface->dev, &dev_attr_output4);
- }
- if (kit->ifkit->outputs >= 8) {
- device_remove_file(&interface->dev, &dev_attr_output5);
- device_remove_file(&interface->dev, &dev_attr_output6);
- device_remove_file(&interface->dev, &dev_attr_output7);
- device_remove_file(&interface->dev, &dev_attr_output8);
- }
- if (kit->ifkit->outputs == 16) {
- device_remove_file(&interface->dev, &dev_attr_output9);
- device_remove_file(&interface->dev, &dev_attr_output10);
- device_remove_file(&interface->dev, &dev_attr_output11);
- device_remove_file(&interface->dev, &dev_attr_output12);
- device_remove_file(&interface->dev, &dev_attr_output13);
- device_remove_file(&interface->dev, &dev_attr_output14);
- device_remove_file(&interface->dev, &dev_attr_output15);
- device_remove_file(&interface->dev, &dev_attr_output16);
- }
+ for (i=0; i<kit->ifkit->outputs; i++)
+ device_remove_file(kit->dev, &dev_output_attrs[i]);
- if (kit->ifkit->inputs >= 4) {
- device_remove_file(&interface->dev, &dev_attr_input1);
- device_remove_file(&interface->dev, &dev_attr_input2);
- device_remove_file(&interface->dev, &dev_attr_input3);
- device_remove_file(&interface->dev, &dev_attr_input4);
- }
- if (kit->ifkit->inputs >= 8) {
- device_remove_file(&interface->dev, &dev_attr_input5);
- device_remove_file(&interface->dev, &dev_attr_input6);
- device_remove_file(&interface->dev, &dev_attr_input7);
- device_remove_file(&interface->dev, &dev_attr_input8);
- }
- if (kit->ifkit->inputs == 16) {
- device_remove_file(&interface->dev, &dev_attr_input9);
- device_remove_file(&interface->dev, &dev_attr_input10);
- device_remove_file(&interface->dev, &dev_attr_input11);
- device_remove_file(&interface->dev, &dev_attr_input12);
- device_remove_file(&interface->dev, &dev_attr_input13);
- device_remove_file(&interface->dev, &dev_attr_input14);
- device_remove_file(&interface->dev, &dev_attr_input15);
- device_remove_file(&interface->dev, &dev_attr_input16);
- }
+ for (i=0; i<kit->ifkit->inputs; i++)
+ device_remove_file(kit->dev, &dev_input_attrs[i]);
- if (kit->ifkit->sensors >= 4) {
- device_remove_file(&interface->dev, &dev_attr_sensor1);
- device_remove_file(&interface->dev, &dev_attr_sensor2);
- device_remove_file(&interface->dev, &dev_attr_sensor3);
- device_remove_file(&interface->dev, &dev_attr_sensor4);
- }
- if (kit->ifkit->sensors >= 7) {
- device_remove_file(&interface->dev, &dev_attr_sensor5);
- device_remove_file(&interface->dev, &dev_attr_sensor6);
- device_remove_file(&interface->dev, &dev_attr_sensor7);
+ for (i=0; i<kit->ifkit->sensors; i++)
+ device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+ if (kit->ifkit->has_lcd) {
+ device_remove_file(kit->dev, &dev_attr_lcd);
+ remove_lcd_files(kit);
}
- if (kit->ifkit->sensors == 8)
- device_remove_file(&interface->dev, &dev_attr_sensor8);
- if (kit->ifkit->has_lcd)
- device_remove_file(&interface->dev, &dev_attr_lcd);
+ device_unregister(kit->dev);
dev_info(&interface->dev, "USB PhidgetInterfaceKit %d/%d/%d detached\n",
kit->ifkit->sensors, kit->ifkit->inputs, kit->ifkit->outputs);
usb_put_dev(kit->udev);
+ clear_bit(kit->dev_no, &device_no);
+
kfree(kit);
}
diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c
new file mode 100644
index 00000000000..6b59b620d61
--- /dev/null
+++ b/drivers/usb/misc/phidgetmotorcontrol.c
@@ -0,0 +1,466 @@
+/*
+ * USB Phidget MotorControl driver
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "phidget.h"
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#define DRIVER_DESC "USB PhidgetMotorControl Driver"
+
+#define USB_VENDOR_ID_GLAB 0x06c2
+#define USB_DEVICE_ID_MOTORCONTROL 0x0058
+
+#define URB_INT_SIZE 8
+
+static unsigned long device_no;
+
+struct motorcontrol {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct device *dev;
+ int dev_no;
+ u8 inputs[4];
+ s8 desired_speed[2];
+ s8 speed[2];
+ s16 _current[2];
+ s8 acceleration[2];
+ struct urb *irq;
+ unsigned char *data;
+ dma_addr_t data_dma;
+
+ struct work_struct do_notify;
+ unsigned long input_events;
+ unsigned long speed_events;
+ unsigned long exceed_events;
+};
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_MOTORCONTROL) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int set_motor(struct motorcontrol *mc, int motor)
+{
+ u8 *buffer;
+ int speed, speed2, acceleration;
+ int retval;
+
+ buffer = kzalloc(8, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&mc->intf->dev, "%s - out of memory\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ acceleration = mc->acceleration[motor] * 10;
+ /* -127 <= speed <= 127 */
+ speed = (mc->desired_speed[motor] * 127) / 100;
+ /* -0x7300 <= speed2 <= 0x7300 */
+ speed2 = (mc->desired_speed[motor] * 230 * 128) / 100;
+
+ buffer[0] = motor;
+ buffer[1] = speed;
+ buffer[2] = acceleration >> 8;
+ buffer[3] = acceleration;
+ buffer[4] = speed2 >> 8;
+ buffer[5] = speed2;
+
+ retval = usb_control_msg(mc->udev,
+ usb_sndctrlpipe(mc->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000);
+
+ if (retval != 8)
+ dev_err(&mc->intf->dev, "usb_control_msg returned %d\n",
+ retval);
+ kfree(buffer);
+
+ return retval < 0 ? retval : 0;
+}
+
+static void motorcontrol_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct motorcontrol *mc = urb->context;
+ unsigned char *buffer = mc->data;
+ int i, level;
+ int status;
+
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ /* -EPIPE: should clear the halt */
+ default: /* error */
+ goto resubmit;
+ }
+
+ /* digital inputs */
+ for (i=0; i<4; i++) {
+ level = (buffer[0] >> i) & 1;
+ if (mc->inputs[i] != level) {
+ mc->inputs[i] = level;
+ set_bit(i, &mc->input_events);
+ }
+ }
+
+ /* motor speed */
+ if (buffer[2] == 0) {
+ for (i=0; i<2; i++) {
+ level = ((s8)buffer[4+i]) * 100 / 127;
+ if (mc->speed[i] != level) {
+ mc->speed[i] = level;
+ set_bit(i, &mc->speed_events);
+ }
+ }
+ } else {
+ int index = buffer[3] & 1;
+
+ level = ((s8)buffer[4] << 8) | buffer[5];
+ level = level * 100 / 29440;
+ if (mc->speed[index] != level) {
+ mc->speed[index] = level;
+ set_bit(index, &mc->speed_events);
+ }
+
+ level = ((s8)buffer[6] << 8) | buffer[7];
+ mc->_current[index] = level * 100 / 1572;
+ }
+
+ if (buffer[1] & 1)
+ set_bit(0, &mc->exceed_events);
+
+ if (buffer[1] & 2)
+ set_bit(1, &mc->exceed_events);
+
+ if (mc->input_events || mc->exceed_events || mc->speed_events)
+ schedule_work(&mc->do_notify);
+
+resubmit:
+ status = usb_submit_urb(urb, SLAB_ATOMIC);
+ if (status)
+ dev_err(&mc->intf->dev,
+ "can't resubmit intr, %s-%s/motorcontrol0, status %d",
+ mc->udev->bus->bus_name,
+ mc->udev->devpath, status);
+}
+
+static void do_notify(void *data)
+{
+ struct motorcontrol *mc = data;
+ int i;
+ char sysfs_file[8];
+
+ for (i=0; i<4; i++) {
+ if (test_and_clear_bit(i, &mc->input_events)) {
+ sprintf(sysfs_file, "input%d", i);
+ sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<2; i++) {
+ if (test_and_clear_bit(i, &mc->speed_events)) {
+ sprintf(sysfs_file, "speed%d", i);
+ sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<2; i++) {
+ if (test_and_clear_bit(i, &mc->exceed_events))
+ dev_warn(&mc->intf->dev,
+ "motor #%d exceeds 1.5 Amp current limit\n", i);
+ }
+}
+
+#define show_set_speed(value) \
+static ssize_t set_speed##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ int speed; \
+ int retval; \
+ \
+ if (sscanf(buf, "%d", &speed) < 1) \
+ return -EINVAL; \
+ \
+ if (speed < -100 || speed > 100) \
+ return -EINVAL; \
+ \
+ mc->desired_speed[value] = speed; \
+ \
+ retval = set_motor(mc, value); \
+ \
+ return retval ? retval : count; \
+} \
+ \
+static ssize_t show_speed##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", mc->speed[value]); \
+}
+
+#define speed_attr(value) \
+ __ATTR(speed##value, S_IWUGO | S_IRUGO, \
+ show_speed##value, set_speed##value)
+
+show_set_speed(0);
+show_set_speed(1);
+
+#define show_set_acceleration(value) \
+static ssize_t set_acceleration##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ int acceleration; \
+ int retval; \
+ \
+ if (sscanf(buf, "%d", &acceleration) < 1) \
+ return -EINVAL; \
+ \
+ if (acceleration < 0 || acceleration > 100) \
+ return -EINVAL; \
+ \
+ mc->acceleration[value] = acceleration; \
+ \
+ retval = set_motor(mc, value); \
+ \
+ return retval ? retval : count; \
+} \
+ \
+static ssize_t show_acceleration##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", mc->acceleration[value]); \
+}
+
+#define acceleration_attr(value) \
+ __ATTR(acceleration##value, S_IWUGO | S_IRUGO, \
+ show_acceleration##value, set_acceleration##value)
+
+show_set_acceleration(0);
+show_set_acceleration(1);
+
+#define show_current(value) \
+static ssize_t show_current##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \
+}
+
+#define current_attr(value) \
+ __ATTR(current##value, S_IRUGO, show_current##value, NULL)
+
+show_current(0);
+show_current(1);
+
+#define show_input(value) \
+static ssize_t show_input##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", (int)mc->inputs[value]); \
+}
+
+#define input_attr(value) \
+ __ATTR(input##value, S_IRUGO, show_input##value, NULL)
+
+show_input(0);
+show_input(1);
+show_input(2);
+show_input(3);
+
+static struct device_attribute dev_attrs[] = {
+ input_attr(0),
+ input_attr(1),
+ input_attr(2),
+ input_attr(3),
+ speed_attr(0),
+ speed_attr(1),
+ acceleration_attr(0),
+ acceleration_attr(1),
+ current_attr(0),
+ current_attr(1)
+};
+
+static int motorcontrol_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct motorcontrol *mc;
+ int pipe, maxp, rc = -ENOMEM;
+ int bit, value, i;
+
+ interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints != 1)
+ return -ENODEV;
+
+ endpoint = &interface->endpoint[0].desc;
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -ENODEV;
+
+ /*
+ * bmAttributes
+ */
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ mc = kzalloc(sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ goto out;
+
+ mc->dev_no = -1;
+ mc->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &mc->data_dma);
+ if (!mc->data)
+ goto out;
+
+ mc->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!mc->irq)
+ goto out;
+
+ mc->udev = usb_get_dev(dev);
+ mc->intf = intf;
+ mc->acceleration[0] = mc->acceleration[1] = 10;
+ INIT_WORK(&mc->do_notify, do_notify, mc);
+ usb_fill_int_urb(mc->irq, mc->udev, pipe, mc->data,
+ maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
+ motorcontrol_irq, mc, endpoint->bInterval);
+ mc->irq->transfer_dma = mc->data_dma;
+ mc->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, mc);
+
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while(value);
+ mc->dev_no = bit;
+
+ mc->dev = device_create(phidget_class, &mc->udev->dev, 0,
+ "motorcontrol%d", mc->dev_no);
+ if (IS_ERR(mc->dev)) {
+ rc = PTR_ERR(mc->dev);
+ mc->dev = NULL;
+ goto out;
+ }
+
+ dev_set_drvdata(mc->dev, mc);
+
+ if (usb_submit_urb(mc->irq, GFP_KERNEL)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ for (i=0; i<ARRAY_SIZE(dev_attrs); i++) {
+ rc = device_create_file(mc->dev, &dev_attrs[i]);
+ if (rc)
+ goto out2;
+ }
+
+ dev_info(&intf->dev, "USB PhidgetMotorControl attached\n");
+
+ return 0;
+out2:
+ while (i-- > 0)
+ device_remove_file(mc->dev, &dev_attrs[i]);
+out:
+ if (mc) {
+ if (mc->irq)
+ usb_free_urb(mc->irq);
+ if (mc->data)
+ usb_buffer_free(dev, URB_INT_SIZE, mc->data, mc->data_dma);
+ if (mc->dev)
+ device_unregister(mc->dev);
+ if (mc->dev_no >= 0)
+ clear_bit(mc->dev_no, &device_no);
+
+ kfree(mc);
+ }
+
+ return rc;
+}
+
+static void motorcontrol_disconnect(struct usb_interface *interface)
+{
+ struct motorcontrol *mc;
+ int i;
+
+ mc = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!mc)
+ return;
+
+ usb_kill_urb(mc->irq);
+ usb_free_urb(mc->irq);
+ usb_buffer_free(mc->udev, URB_INT_SIZE, mc->data, mc->data_dma);
+
+ cancel_delayed_work(&mc->do_notify);
+
+ for (i=0; i<ARRAY_SIZE(dev_attrs); i++)
+ device_remove_file(mc->dev, &dev_attrs[i]);
+
+ device_unregister(mc->dev);
+
+ usb_put_dev(mc->udev);
+ clear_bit(mc->dev_no, &device_no);
+ kfree(mc);
+
+ dev_info(&interface->dev, "USB PhidgetMotorControl detached\n");
+}
+
+static struct usb_driver motorcontrol_driver = {
+ .name = "phidgetmotorcontrol",
+ .probe = motorcontrol_probe,
+ .disconnect = motorcontrol_disconnect,
+ .id_table = id_table
+};
+
+static int __init motorcontrol_init(void)
+{
+ int retval = 0;
+
+ retval = usb_register(&motorcontrol_driver);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+
+ return retval;
+}
+
+static void __exit motorcontrol_exit(void)
+{
+ usb_deregister(&motorcontrol_driver);
+}
+
+module_init(motorcontrol_init);
+module_exit(motorcontrol_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c
index c0df79c9653..7163f05c5b2 100644
--- a/drivers/usb/misc/phidgetservo.c
+++ b/drivers/usb/misc/phidgetservo.c
@@ -1,7 +1,7 @@
/*
* USB PhidgetServo driver 1.0
*
- * Copyright (C) 2004 Sean Young <sean@mess.org>
+ * Copyright (C) 2004, 2006 Sean Young <sean@mess.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,14 +15,6 @@
*
* CAUTION: Generally you should use 0 < degrees < 180 as anything else
* is probably beyond the range of your servo and may damage it.
- *
- * Jun 16, 2004: Sean Young <sean@mess.org>
- * - cleanups
- * - was using memory after kfree()
- * Aug 8, 2004: Sean Young <sean@mess.org>
- * - set the highest angle as high as the hardware allows, there are
- * some odd servos out there
- *
*/
#include <linux/kernel.h>
@@ -32,6 +24,8 @@
#include <linux/module.h>
#include <linux/usb.h>
+#include "phidget.h"
+
#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
#define DRIVER_DESC "USB PhidgetServo Driver"
@@ -70,8 +64,12 @@ static struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table);
+static int unsigned long device_no;
+
struct phidget_servo {
struct usb_device *udev;
+ struct device *dev;
+ int dev_no;
ulong type;
int pulse[4];
int degrees[4];
@@ -203,16 +201,16 @@ change_position_v20(struct phidget_servo *servo, int servo_no, int degrees,
}
#define show_set(value) \
-static ssize_t set_servo##value (struct device *dev, struct device_attribute *attr, \
+static ssize_t set_servo##value (struct device *dev, \
+ struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
int degrees, minutes, retval; \
- struct usb_interface *intf = to_usb_interface (dev); \
- struct phidget_servo *servo = usb_get_intfdata (intf); \
+ struct phidget_servo *servo = dev_get_drvdata(dev); \
\
minutes = 0; \
/* must at least convert degrees */ \
- if (sscanf (buf, "%d.%d", &degrees, &minutes) < 1) { \
+ if (sscanf(buf, "%d.%d", &degrees, &minutes) < 1) { \
return -EINVAL; \
} \
\
@@ -220,86 +218,127 @@ static ssize_t set_servo##value (struct device *dev, struct device_attribute *at
return -EINVAL; \
\
if (servo->type & SERVO_VERSION_30) \
- retval = change_position_v30 (servo, value, degrees, \
+ retval = change_position_v30(servo, value, degrees, \
minutes); \
else \
- retval = change_position_v20 (servo, value, degrees, \
+ retval = change_position_v20(servo, value, degrees, \
minutes); \
\
return retval < 0 ? retval : count; \
} \
\
-static ssize_t show_servo##value (struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_servo##value (struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface (dev); \
- struct phidget_servo *servo = usb_get_intfdata (intf); \
+ struct phidget_servo *servo = dev_get_drvdata(dev); \
\
- return sprintf (buf, "%d.%02d\n", servo->degrees[value], \
+ return sprintf(buf, "%d.%02d\n", servo->degrees[value], \
servo->minutes[value]); \
-} \
-static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \
- show_servo##value, set_servo##value);
+}
+#define servo_attr(value) \
+ __ATTR(servo##value, S_IWUGO | S_IRUGO, \
+ show_servo##value, set_servo##value)
show_set(0);
show_set(1);
show_set(2);
show_set(3);
+static struct device_attribute dev_attrs[] = {
+ servo_attr(0), servo_attr(1), servo_attr(2), servo_attr(3)
+};
+
static int
servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct phidget_servo *dev;
+ int bit, value, rc;
+ int servo_count, i;
dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto out;
}
dev->udev = usb_get_dev(udev);
dev->type = id->driver_info;
+ dev->dev_no = -1;
usb_set_intfdata(interface, dev);
- device_create_file(&interface->dev, &dev_attr_servo0);
- if (dev->type & SERVO_COUNT_QUAD) {
- device_create_file(&interface->dev, &dev_attr_servo1);
- device_create_file(&interface->dev, &dev_attr_servo2);
- device_create_file(&interface->dev, &dev_attr_servo3);
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while (value);
+ dev->dev_no = bit;
+
+ dev->dev = device_create(phidget_class, &dev->udev->dev, 0,
+ "servo%d", dev->dev_no);
+ if (IS_ERR(dev->dev)) {
+ rc = PTR_ERR(dev->dev);
+ dev->dev = NULL;
+ goto out;
+ }
+
+ servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+ for (i=0; i<servo_count; i++) {
+ rc = device_create_file(dev->dev, &dev_attrs[i]);
+ if (rc)
+ goto out2;
}
dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n",
- dev->type & SERVO_COUNT_QUAD ? 4 : 1,
- dev->type & SERVO_VERSION_30 ? 3 : 2);
+ servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
- if(!(dev->type & SERVO_VERSION_30))
+ if (!(dev->type & SERVO_VERSION_30))
dev_info(&interface->dev,
"WARNING: v2.0 not tested! Please report if it works.\n");
return 0;
+out2:
+ while (i-- > 0)
+ device_remove_file(dev->dev, &dev_attrs[i]);
+out:
+ if (dev) {
+ if (dev->dev)
+ device_unregister(dev->dev);
+ if (dev->dev_no >= 0)
+ clear_bit(dev->dev_no, &device_no);
+
+ kfree(dev);
+ }
+
+ return rc;
}
static void
servo_disconnect(struct usb_interface *interface)
{
struct phidget_servo *dev;
+ int servo_count, i;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
- device_remove_file(&interface->dev, &dev_attr_servo0);
- if (dev->type & SERVO_COUNT_QUAD) {
- device_remove_file(&interface->dev, &dev_attr_servo1);
- device_remove_file(&interface->dev, &dev_attr_servo2);
- device_remove_file(&interface->dev, &dev_attr_servo3);
- }
+ if (!dev)
+ return;
+
+ servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+ for (i=0; i<servo_count; i++)
+ device_remove_file(dev->dev, &dev_attrs[i]);
+ device_unregister(dev->dev);
usb_put_dev(dev->udev);
dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
- dev->type & SERVO_COUNT_QUAD ? 4 : 1,
- dev->type & SERVO_VERSION_30 ? 3 : 2);
+ servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
+ clear_bit(dev->dev_no, &device_no);
kfree(dev);
}
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index e16582f3733..a44124c7e85 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -3179,7 +3179,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
}
#endif
-static struct file_operations usb_sisusb_fops = {
+static const struct file_operations usb_sisusb_fops = {
.owner = THIS_MODULE,
.open = sisusb_open,
.release = sisusb_release,
diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h
new file mode 100644
index 00000000000..551ba8906d6
--- /dev/null
+++ b/drivers/usb/misc/usb_u132.h
@@ -0,0 +1,97 @@
+/*
+* Common Header File for the Elan Digital Systems U132 adapter
+* this file should be included by both the "ftdi-u132" and
+* the "u132-hcd" modules.
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+*(http://www.elandigitalsystems.com)
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+*(tony.olech@elandigitalsystems.com)
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB client drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+* The driver consists of two modules, the "ftdi-u132" module is
+* a USB client driver that interfaces to the FTDI chip within
+* the U132 adapter manufactured by Elan Digital Systems, and the
+* "u132-hcd" module is a USB host controller driver that talks
+* to the OHCI controller within CardBus card that are inserted
+* in the U132 adapter.
+*
+* The "ftdi-u132" module should be loaded automatically by the
+* hot plug system when the U132 adapter is plugged in. The module
+* initialises the adapter which mostly consists of synchronising
+* the FTDI chip, before continuously polling the adapter to detect
+* PC card insertions. As soon as a PC card containing a recognised
+* OHCI controller is seen the "ftdi-u132" module explicitly requests
+* the kernel to load the "u132-hcd" module.
+*
+* The "ftdi-u132" module provides the interface to the inserted
+* PC card and the "u132-hcd" module uses the API to send and recieve
+* data. The API features call-backs, so that part of the "u132-hcd"
+* module code will run in the context of one of the kernel threads
+* of the "ftdi-u132" module.
+*
+*/
+int ftdi_elan_switch_on_diagnostics(int number);
+void ftdi_elan_gone_away(struct platform_device *pdev);
+void start_usb_lock_device_tracing(void);
+struct u132_platform_data {
+ u16 vendor;
+ u16 device;
+ u8 potpg;
+ void (*port_power) (struct device *dev, int is_on);
+ void (*reset) (struct device *dev);
+};
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+ void *endp);
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index e095772dd8e..dbaca9f1efa 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -239,7 +239,7 @@ error:
return retval;
}
-static struct file_operations lcd_fops = {
+static const struct file_operations lcd_fops = {
.owner = THIS_MODULE,
.read = lcd_read,
.write = lcd_write,
@@ -290,9 +290,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
- (endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
dev->bulk_in_size = buffer_size;
@@ -305,9 +303,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
}
if (!dev->bulk_out_endpointAddr &&
- !(endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c
index 0c5ee0ad6bb..49c5c5c4c43 100644
--- a/drivers/usb/misc/usbled.c
+++ b/drivers/usb/misc/usbled.c
@@ -108,22 +108,34 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id
dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
- goto error;
+ goto error_mem;
}
dev->udev = usb_get_dev(udev);
usb_set_intfdata (interface, dev);
- device_create_file(&interface->dev, &dev_attr_blue);
- device_create_file(&interface->dev, &dev_attr_red);
- device_create_file(&interface->dev, &dev_attr_green);
+ retval = device_create_file(&interface->dev, &dev_attr_blue);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_red);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_green);
+ if (retval)
+ goto error;
dev_info(&interface->dev, "USB LED device now attached\n");
return 0;
error:
+ device_remove_file(&interface->dev, &dev_attr_blue);
+ device_remove_file(&interface->dev, &dev_attr_red);
+ device_remove_file(&interface->dev, &dev_attr_green);
+ usb_set_intfdata (interface, NULL);
+ usb_put_dev(dev->udev);
kfree(dev);
+error_mem:
return retval;
}
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 275a66f8305..394bbf2f68d 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -265,7 +265,6 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
ubus->mon_bus = NULL;
mbus->u_bus = NULL;
mb();
- // usb_bus_put(ubus);
}
/*
@@ -297,12 +296,12 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
INIT_LIST_HEAD(&mbus->r_list);
/*
- * This usb_bus_get here is superfluous, because we receive
- * a notification if usb_bus is about to be removed.
+ * We don't need to take a reference to ubus, because we receive
+ * a notification if the bus is about to be removed.
*/
- // usb_bus_get(ubus);
mbus->u_bus = ubus;
ubus->mon_bus = mbus;
+ mbus->uses_dma = ubus->uses_dma;
rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
if (rc <= 0 || rc >= NAMESZ)
diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c
index 1fe01d994a7..f6d1491256c 100644
--- a/drivers/usb/mon/mon_stat.c
+++ b/drivers/usb/mon/mon_stat.c
@@ -28,7 +28,7 @@ static int mon_stat_open(struct inode *inode, struct file *file)
if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL)
return -ENOMEM;
- mbus = inode->u.generic_ip;
+ mbus = inode->i_private;
sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
"nreaders %d events %u text_lost %u\n",
@@ -62,7 +62,7 @@ static int mon_stat_release(struct inode *inode, struct file *file)
return 0;
}
-struct file_operations mon_fops_stat = {
+const struct file_operations mon_fops_stat = {
.owner = THIS_MODULE,
.open = mon_stat_open,
.llseek = no_llseek,
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index f961a770cee..7a2346c5328 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -75,13 +75,13 @@ static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
*/
static inline char mon_text_get_setup(struct mon_event_text *ep,
- struct urb *urb, char ev_type)
+ struct urb *urb, char ev_type, struct mon_bus *mbus)
{
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
return '-';
- if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+ if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
if (urb->setup_packet == NULL)
return 'Z'; /* '0' would be not as pretty. */
@@ -91,7 +91,7 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
}
static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
- int len, char ev_type)
+ int len, char ev_type, struct mon_bus *mbus)
{
int pipe = urb->pipe;
@@ -117,7 +117,7 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
* contain non-NULL garbage in case the upper level promised to
* set DMA for the HCD.
*/
- if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ if (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
return mon_dmapeek(ep->data, urb->transfer_dma, len);
if (urb->transfer_buffer == NULL)
@@ -161,8 +161,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
/* Collecting status makes debugging sense for submits, too */
ep->status = urb->status;
- ep->setup_flag = mon_text_get_setup(ep, urb, ev_type);
- ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type);
+ ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
+ ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type,
+ rp->r.m_bus);
rp->nevents++;
list_add_tail(&ep->e_link, &rp->e_list);
@@ -238,7 +239,7 @@ static int mon_text_open(struct inode *inode, struct file *file)
int rc;
mutex_lock(&mon_lock);
- mbus = inode->u.generic_ip;
+ mbus = inode->i_private;
ubus = mbus->u_bus;
rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
@@ -401,7 +402,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
struct mon_event_text *ep;
mutex_lock(&mon_lock);
- mbus = inode->u.generic_ip;
+ mbus = inode->i_private;
if (mbus->nreaders <= 0) {
printk(KERN_ERR TAG ": consistency error on close\n");
@@ -435,7 +436,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
return 0;
}
-struct file_operations mon_fops_text = {
+const struct file_operations mon_fops_text = {
.owner = THIS_MODULE,
.open = mon_text_open,
.llseek = no_llseek,
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index 33678c24ebe..ab9d02d5df7 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -20,6 +20,7 @@ struct mon_bus {
struct dentry *dent_s; /* Debugging file */
struct dentry *dent_t; /* Text interface file */
struct usb_bus *u_bus;
+ int uses_dma;
/* Ref */
int nreaders; /* Under mon_lock AND mbus->lock */
@@ -53,7 +54,7 @@ extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
extern struct mutex mon_lock;
-extern struct file_operations mon_fops_text;
-extern struct file_operations mon_fops_stat;
+extern const struct file_operations mon_fops_text;
+extern const struct file_operations mon_fops_stat;
#endif /* __USB_MON_H */
diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c
index 2e2bbc003e9..9c0eacf7055 100644
--- a/drivers/usb/net/asix.c
+++ b/drivers/usb/net/asix.c
@@ -1,7 +1,8 @@
/*
* ASIX AX8817X based USB 2.0 Ethernet Devices
- * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com>
* Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (C) 2006 James Painter <jamie.painter@iname.com>
* Copyright (c) 2002-2003 TiVo Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -36,6 +37,9 @@
#include "usbnet.h"
+#define DRIVER_VERSION "14-Jun-2006"
+static const char driver_name [] = "asix";
+
/* ASIX AX8817X based USB 2.0 Ethernet Devices */
#define AX_CMD_SET_SW_MII 0x06
@@ -46,23 +50,25 @@
#define AX_CMD_WRITE_EEPROM 0x0c
#define AX_CMD_WRITE_ENABLE 0x0d
#define AX_CMD_WRITE_DISABLE 0x0e
+#define AX_CMD_READ_RX_CTL 0x0f
#define AX_CMD_WRITE_RX_CTL 0x10
#define AX_CMD_READ_IPG012 0x11
#define AX_CMD_WRITE_IPG0 0x12
#define AX_CMD_WRITE_IPG1 0x13
+#define AX_CMD_READ_NODE_ID 0x13
#define AX_CMD_WRITE_IPG2 0x14
#define AX_CMD_WRITE_MULTI_FILTER 0x16
-#define AX_CMD_READ_NODE_ID 0x17
+#define AX88172_CMD_READ_NODE_ID 0x17
#define AX_CMD_READ_PHY_ID 0x19
#define AX_CMD_READ_MEDIUM_STATUS 0x1a
#define AX_CMD_WRITE_MEDIUM_MODE 0x1b
#define AX_CMD_READ_MONITOR_MODE 0x1c
#define AX_CMD_WRITE_MONITOR_MODE 0x1d
+#define AX_CMD_READ_GPIOS 0x1e
#define AX_CMD_WRITE_GPIOS 0x1f
#define AX_CMD_SW_RESET 0x20
#define AX_CMD_SW_PHY_STATUS 0x21
#define AX_CMD_SW_PHY_SELECT 0x22
-#define AX88772_CMD_READ_NODE_ID 0x13
#define AX_MONITOR_MODE 0x01
#define AX_MONITOR_LINK 0x02
@@ -70,15 +76,15 @@
#define AX_MONITOR_HSFS 0x10
/* AX88172 Medium Status Register values */
-#define AX_MEDIUM_FULL_DUPLEX 0x02
-#define AX_MEDIUM_TX_ABORT_ALLOW 0x04
-#define AX_MEDIUM_FLOW_CONTROL_EN 0x10
+#define AX88172_MEDIUM_FD 0x02
+#define AX88172_MEDIUM_TX 0x04
+#define AX88172_MEDIUM_FC 0x10
+#define AX88172_MEDIUM_DEFAULT \
+ ( AX88172_MEDIUM_FD | AX88172_MEDIUM_TX | AX88172_MEDIUM_FC )
#define AX_MCAST_FILTER_SIZE 8
#define AX_MAX_MCAST 64
-#define AX_EEPROM_LEN 0x40
-
#define AX_SWRESET_CLEAR 0x00
#define AX_SWRESET_RR 0x01
#define AX_SWRESET_RT 0x02
@@ -92,23 +98,78 @@
#define AX88772_IPG1_DEFAULT 0x0c
#define AX88772_IPG2_DEFAULT 0x12
-#define AX88772_MEDIUM_FULL_DUPLEX 0x0002
-#define AX88772_MEDIUM_RESERVED 0x0004
-#define AX88772_MEDIUM_RX_FC_ENABLE 0x0010
-#define AX88772_MEDIUM_TX_FC_ENABLE 0x0020
-#define AX88772_MEDIUM_PAUSE_FORMAT 0x0080
-#define AX88772_MEDIUM_RX_ENABLE 0x0100
-#define AX88772_MEDIUM_100MB 0x0200
-#define AX88772_MEDIUM_DEFAULT \
- (AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
- AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
- AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE )
+/* AX88772 & AX88178 Medium Mode Register */
+#define AX_MEDIUM_PF 0x0080
+#define AX_MEDIUM_JFE 0x0040
+#define AX_MEDIUM_TFC 0x0020
+#define AX_MEDIUM_RFC 0x0010
+#define AX_MEDIUM_ENCK 0x0008
+#define AX_MEDIUM_AC 0x0004
+#define AX_MEDIUM_FD 0x0002
+#define AX_MEDIUM_GM 0x0001
+#define AX_MEDIUM_SM 0x1000
+#define AX_MEDIUM_SBP 0x0800
+#define AX_MEDIUM_PS 0x0200
+#define AX_MEDIUM_RE 0x0100
+
+#define AX88178_MEDIUM_DEFAULT \
+ (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \
+ AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \
+ AX_MEDIUM_RE )
-#define AX_EEPROM_MAGIC 0xdeadbeef
+#define AX88772_MEDIUM_DEFAULT \
+ (AX_MEDIUM_FD | AX_MEDIUM_RFC | \
+ AX_MEDIUM_TFC | AX_MEDIUM_PS | \
+ AX_MEDIUM_AC | AX_MEDIUM_RE )
+
+/* AX88772 & AX88178 RX_CTL values */
+#define AX_RX_CTL_SO 0x0080
+#define AX_RX_CTL_AP 0x0020
+#define AX_RX_CTL_AM 0x0010
+#define AX_RX_CTL_AB 0x0008
+#define AX_RX_CTL_SEP 0x0004
+#define AX_RX_CTL_AMALL 0x0002
+#define AX_RX_CTL_PRO 0x0001
+#define AX_RX_CTL_MFB_2048 0x0000
+#define AX_RX_CTL_MFB_4096 0x0100
+#define AX_RX_CTL_MFB_8192 0x0200
+#define AX_RX_CTL_MFB_16384 0x0300
+
+#define AX_DEFAULT_RX_CTL \
+ (AX_RX_CTL_SO | AX_RX_CTL_AB )
+
+/* GPIO 0 .. 2 toggles */
+#define AX_GPIO_GPO0EN 0x01 /* GPIO0 Output enable */
+#define AX_GPIO_GPO_0 0x02 /* GPIO0 Output value */
+#define AX_GPIO_GPO1EN 0x04 /* GPIO1 Output enable */
+#define AX_GPIO_GPO_1 0x08 /* GPIO1 Output value */
+#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */
+#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */
+#define AX_GPIO_RESERVED 0x40 /* Reserved */
+#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */
+
+#define AX_EEPROM_MAGIC 0xdeadbeef
+#define AX88172_EEPROM_LEN 0x40
+#define AX88772_EEPROM_LEN 0xff
+
+#define PHY_MODE_MARVELL 0x0000
+#define MII_MARVELL_LED_CTRL 0x0018
+#define MII_MARVELL_STATUS 0x001b
+#define MII_MARVELL_CTRL 0x0014
+
+#define MARVELL_LED_MANUAL 0x0019
+
+#define MARVELL_STATUS_HWCFG 0x0004
+
+#define MARVELL_CTRL_TXDELAY 0x0002
+#define MARVELL_CTRL_RXDELAY 0x0080
/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
struct asix_data {
u8 multi_filter[AX_MCAST_FILTER_SIZE];
+ u8 phymode;
+ u8 ledmode;
+ u8 eeprom_len;
};
struct ax88172_int_data {
@@ -122,6 +183,8 @@ struct ax88172_int_data {
static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
u16 size, void *data)
{
+ devdbg(dev,"asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+ cmd, value, index, size);
return usb_control_msg(
dev->udev,
usb_rcvctrlpipe(dev->udev, 0),
@@ -137,6 +200,8 @@ static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
static int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
u16 size, void *data)
{
+ devdbg(dev,"asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+ cmd, value, index, size);
return usb_control_msg(
dev->udev,
usb_sndctrlpipe(dev->udev, 0),
@@ -161,12 +226,167 @@ static void asix_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
usb_free_urb(urb);
}
+static void
+asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+ u16 size, void *data)
+{
+ struct usb_ctrlrequest *req;
+ int status;
+ struct urb *urb;
+
+ devdbg(dev,"asix_write_cmd_async() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+ cmd, value, index, size);
+ if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
+ deverr(dev, "Error allocating URB in write_cmd_async!");
+ return;
+ }
+
+ if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
+ deverr(dev, "Failed to allocate memory for control request");
+ usb_free_urb(urb);
+ return;
+ }
+
+ req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ req->bRequest = cmd;
+ req->wValue = value;
+ req->wIndex = index;
+ req->wLength = size;
+
+ usb_fill_control_urb(urb, dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ (void *)req, data, size,
+ asix_async_cmd_callback, req);
+
+ if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ deverr(dev, "Error submitting the control message: status=%d",
+ status);
+ kfree(req);
+ usb_free_urb(urb);
+ }
+}
+
+static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ u8 *head;
+ u32 header;
+ char *packet;
+ struct sk_buff *ax_skb;
+ u16 size;
+
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+ packet = head + sizeof(header);
+
+ skb_pull(skb, 4);
+
+ while (skb->len > 0) {
+ if ((short)(header & 0x0000ffff) !=
+ ~((short)((header & 0xffff0000) >> 16))) {
+ deverr(dev,"asix_rx_fixup() Bad Header Length");
+ }
+ /* get the packet length */
+ size = (u16) (header & 0x0000ffff);
+
+ if ((skb->len) - ((size + 1) & 0xfffe) == 0)
+ return 2;
+ if (size > ETH_FRAME_LEN) {
+ deverr(dev,"asix_rx_fixup() Bad RX Length %d", size);
+ return 0;
+ }
+ ax_skb = skb_clone(skb, GFP_ATOMIC);
+ if (ax_skb) {
+ ax_skb->len = size;
+ ax_skb->data = packet;
+ ax_skb->tail = packet + size;
+ usbnet_skb_return(dev, ax_skb);
+ } else {
+ return 0;
+ }
+
+ skb_pull(skb, (size + 1) & 0xfffe);
+
+ if (skb->len == 0)
+ break;
+
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+ packet = head + sizeof(header);
+ skb_pull(skb, 4);
+ }
+
+ if (skb->len < 0) {
+ deverr(dev,"asix_rx_fixup() Bad SKB Length %d", skb->len);
+ return 0;
+ }
+ return 1;
+}
+
+static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+ gfp_t flags)
+{
+ int padlen;
+ int headroom = skb_headroom(skb);
+ int tailroom = skb_tailroom(skb);
+ u32 packet_len;
+ u32 padbytes = 0xffff0000;
+
+ padlen = ((skb->len + 4) % 512) ? 0 : 4;
+
+ if ((!skb_cloned(skb))
+ && ((headroom + tailroom) >= (4 + padlen))) {
+ if ((headroom < 4) || (tailroom < padlen)) {
+ skb->data = memmove(skb->head + 4, skb->data, skb->len);
+ skb->tail = skb->data + skb->len;
+ }
+ } else {
+ struct sk_buff *skb2;
+ skb2 = skb_copy_expand(skb, 4, padlen, flags);
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+ if (!skb)
+ return NULL;
+ }
+
+ skb_push(skb, 4);
+ packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+ memcpy(skb->data, &packet_len, sizeof(packet_len));
+
+ if ((skb->len % 512) == 0) {
+ memcpy( skb->tail, &padbytes, sizeof(padbytes));
+ skb_put(skb, sizeof(padbytes));
+ }
+ return skb;
+}
+
+static void asix_status(struct usbnet *dev, struct urb *urb)
+{
+ struct ax88172_int_data *event;
+ int link;
+
+ if (urb->actual_length < 8)
+ return;
+
+ event = urb->transfer_buffer;
+ link = event->link & 0x01;
+ if (netif_carrier_ok(dev->net) != link) {
+ if (link) {
+ netif_carrier_on(dev->net);
+ usbnet_defer_kevent (dev, EVENT_LINK_RESET );
+ } else
+ netif_carrier_off(dev->net);
+ devdbg(dev, "Link Status is: %d", link);
+ }
+}
+
static inline int asix_set_sw_mii(struct usbnet *dev)
{
int ret;
ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
if (ret < 0)
- devdbg(dev, "Failed to enable software MII access");
+ deverr(dev, "Failed to enable software MII access");
return ret;
}
@@ -175,24 +395,27 @@ static inline int asix_set_hw_mii(struct usbnet *dev)
int ret;
ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL);
if (ret < 0)
- devdbg(dev, "Failed to enable hardware MII access");
+ deverr(dev, "Failed to enable hardware MII access");
return ret;
}
-static inline int asix_get_phyid(struct usbnet *dev)
+static inline int asix_get_phy_addr(struct usbnet *dev)
{
int ret = 0;
void *buf;
+ devdbg(dev, "asix_get_phy_addr()");
+
buf = kmalloc(2, GFP_KERNEL);
if (!buf)
goto out1;
if ((ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID,
0, 0, 2, buf)) < 2) {
- devdbg(dev, "Error reading PHYID register: %02x", ret);
+ deverr(dev, "Error reading PHYID register: %02x", ret);
goto out2;
}
+ devdbg(dev, "asix_get_phy_addr() returning 0x%04x", *((u16 *)buf));
ret = *((u8 *)buf + 1);
out2:
kfree(buf);
@@ -206,91 +429,109 @@ static int asix_sw_reset(struct usbnet *dev, u8 flags)
ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL);
if (ret < 0)
- devdbg(dev,"Failed to send software reset: %02x", ret);
+ deverr(dev,"Failed to send software reset: %02x", ret);
return ret;
}
+static u16 asix_read_rx_ctl(struct usbnet *dev)
+{
+ u16 ret = 0;
+ void *buf;
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ goto out1;
+
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL,
+ 0, 0, 2, buf)) < 2) {
+ deverr(dev, "Error reading RX_CTL register: %02x", ret);
+ goto out2;
+ }
+ ret = le16_to_cpu(*((u16 *)buf));
+out2:
+ kfree(buf);
+out1:
+ return ret;
+}
+
static int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
{
int ret;
+ devdbg(dev,"asix_write_rx_ctl() - mode = 0x%04x", mode);
ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL);
if (ret < 0)
- devdbg(dev, "Failed to write RX_CTL mode: %02x", ret);
+ deverr(dev, "Failed to write RX_CTL mode to 0x%04x: %02x",
+ mode, ret);
return ret;
}
-static void asix_status(struct usbnet *dev, struct urb *urb)
+static u16 asix_read_medium_status(struct usbnet *dev)
{
- struct ax88172_int_data *event;
- int link;
+ u16 ret = 0;
+ void *buf;
- if (urb->actual_length < 8)
- return;
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ goto out1;
- event = urb->transfer_buffer;
- link = event->link & 0x01;
- if (netif_carrier_ok(dev->net) != link) {
- if (link) {
- netif_carrier_on(dev->net);
- usbnet_defer_kevent (dev, EVENT_LINK_RESET );
- } else
- netif_carrier_off(dev->net);
- devdbg(dev, "Link Status is: %d", link);
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS,
+ 0, 0, 2, buf)) < 2) {
+ deverr(dev, "Error reading Medium Status register: %02x", ret);
+ goto out2;
}
+ ret = le16_to_cpu(*((u16 *)buf));
+out2:
+ kfree(buf);
+out1:
+ return ret;
}
-static void
-asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
- u16 size, void *data)
+static int asix_write_medium_mode(struct usbnet *dev, u16 mode)
{
- struct usb_ctrlrequest *req;
- int status;
- struct urb *urb;
+ int ret;
- if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
- devdbg(dev, "Error allocating URB in write_cmd_async!");
- return;
- }
+ devdbg(dev,"asix_write_medium_mode() - mode = 0x%04x", mode);
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+ if (ret < 0)
+ deverr(dev, "Failed to write Medium Mode mode to 0x%04x: %02x",
+ mode, ret);
- if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
- deverr(dev, "Failed to allocate memory for control request");
- usb_free_urb(urb);
- return;
- }
+ return ret;
+}
- req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
- req->bRequest = cmd;
- req->wValue = cpu_to_le16(value);
- req->wIndex = cpu_to_le16(index);
- req->wLength = cpu_to_le16(size);
+static int asix_write_gpio(struct usbnet *dev, u16 value, int sleep)
+{
+ int ret;
- usb_fill_control_urb(urb, dev->udev,
- usb_sndctrlpipe(dev->udev, 0),
- (void *)req, data, size,
- asix_async_cmd_callback, req);
+ devdbg(dev,"asix_write_gpio() - value = 0x%04x", value);
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL);
+ if (ret < 0)
+ deverr(dev, "Failed to write GPIO value 0x%04x: %02x",
+ value, ret);
- if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- deverr(dev, "Error submitting the control message: status=%d",
- status);
- kfree(req);
- usb_free_urb(urb);
- }
+ if (sleep)
+ msleep(sleep);
+
+ return ret;
}
+/*
+ * AX88772 & AX88178 have a 16-bit RX_CTL value
+ */
static void asix_set_multicast(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
struct asix_data *data = (struct asix_data *)&dev->data;
- u8 rx_ctl = 0x8c;
+ u16 rx_ctl = AX_DEFAULT_RX_CTL;
if (net->flags & IFF_PROMISC) {
- rx_ctl |= 0x01;
+ rx_ctl |= AX_RX_CTL_PRO;
} else if (net->flags & IFF_ALLMULTI
|| net->mc_count > AX_MAX_MCAST) {
- rx_ctl |= 0x02;
+ rx_ctl |= AX_RX_CTL_AMALL;
} else if (net->mc_count == 0) {
/* just broadcast and directed */
} else {
@@ -317,7 +558,7 @@ static void asix_set_multicast(struct net_device *net)
asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
AX_MCAST_FILTER_SIZE, data->multi_filter);
- rx_ctl |= 0x10;
+ rx_ctl |= AX_RX_CTL_AM;
}
asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
@@ -333,50 +574,43 @@ static int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
(__u16)loc, 2, (u16 *)&res);
asix_set_hw_mii(dev);
- return res & 0xffff;
-}
+ devdbg(dev, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", phy_id, loc, le16_to_cpu(res & 0xffff));
-/* same as above, but converts resulting value to cpu byte order */
-static int asix_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
-{
- return le16_to_cpu(asix_mdio_read(netdev,phy_id, loc));
+ return le16_to_cpu(res & 0xffff);
}
static void
asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
{
struct usbnet *dev = netdev_priv(netdev);
- u16 res = val;
+ u16 res = cpu_to_le16(val);
+ devdbg(dev, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", phy_id, loc, val);
asix_set_sw_mii(dev);
asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
(__u16)loc, 2, (u16 *)&res);
asix_set_hw_mii(dev);
}
-/* same as above, but converts new value to le16 byte order before writing */
-static void
-asix_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
+static u32 asix_get_phyid(struct usbnet *dev)
{
- asix_mdio_write( netdev, phy_id, loc, cpu_to_le16(val) );
-}
+ int phy_reg;
+ u32 phy_id;
-static int ax88172_link_reset(struct usbnet *dev)
-{
- u16 lpa;
- u16 adv;
- u16 res;
- u8 mode;
+ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
+ if (phy_reg < 0)
+ return 0;
- mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
- lpa = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
- adv = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
- res = mii_nway_result(lpa|adv);
- if (res & LPA_DUPLEX)
- mode |= AX_MEDIUM_FULL_DUPLEX;
- asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+ phy_id = (phy_reg & 0xffff) << 16;
- return 0;
+ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2);
+ if (phy_reg < 0)
+ return 0;
+
+ phy_id |= (phy_reg & 0xffff);
+
+ return phy_id;
}
static void
@@ -423,7 +657,10 @@ asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
static int asix_get_eeprom_len(struct net_device *net)
{
- return AX_EEPROM_LEN;
+ struct usbnet *dev = netdev_priv(net);
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
+ return data->eeprom_len;
}
static int asix_get_eeprom(struct net_device *net,
@@ -453,9 +690,14 @@ static int asix_get_eeprom(struct net_device *net,
static void asix_get_drvinfo (struct net_device *net,
struct ethtool_drvinfo *info)
{
+ struct usbnet *dev = netdev_priv(net);
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
/* Inherit standard device info */
usbnet_get_drvinfo(net, info);
- info->eedump_len = 0x3e;
+ strncpy (info->driver, driver_name, sizeof info->driver);
+ strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+ info->eedump_len = data->eeprom_len;
}
static int asix_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
@@ -468,8 +710,34 @@ static int asix_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
static int asix_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
{
struct usbnet *dev = netdev_priv(net);
+ int res = mii_ethtool_sset(&dev->mii,cmd);
+
+ /* link speed/duplex might have changed */
+ if (dev->driver_info->link_reset)
+ dev->driver_info->link_reset(dev);
+
+ return res;
+}
+
+static int asix_nway_reset(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return mii_nway_restart(&dev->mii);
+}
+
+static u32 asix_get_link(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
- return mii_ethtool_sset(&dev->mii,cmd);
+ return mii_link_ok(&dev->mii);
+}
+
+static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
}
/* We need to override some ethtool_ops so we require our
@@ -477,7 +745,8 @@ static int asix_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
devices that may be connected at the same time. */
static struct ethtool_ops ax88172_ethtool_ops = {
.get_drvinfo = asix_get_drvinfo,
- .get_link = ethtool_op_get_link,
+ .get_link = asix_get_link,
+ .nway_reset = asix_nway_reset,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
.get_wol = asix_get_wol,
@@ -488,11 +757,66 @@ static struct ethtool_ops ax88172_ethtool_ops = {
.set_settings = asix_set_settings,
};
-static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+static void ax88172_set_multicast(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ u8 rx_ctl = 0x8c;
- return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+ if (net->flags & IFF_PROMISC) {
+ rx_ctl |= 0x01;
+ } else if (net->flags & IFF_ALLMULTI
+ || net->mc_count > AX_MAX_MCAST) {
+ rx_ctl |= 0x02;
+ } else if (net->mc_count == 0) {
+ /* just broadcast and directed */
+ } else {
+ /* We use the 20 byte dev->data
+ * for our 8 byte filter buffer
+ * to avoid allocating memory that
+ * is tricky to free later */
+ struct dev_mc_list *mc_list = net->mc_list;
+ u32 crc_bits;
+ int i;
+
+ memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+
+ /* Build the multicast hash filter. */
+ for (i = 0; i < net->mc_count; i++) {
+ crc_bits =
+ ether_crc(ETH_ALEN,
+ mc_list->dmi_addr) >> 26;
+ data->multi_filter[crc_bits >> 3] |=
+ 1 << (crc_bits & 7);
+ mc_list = mc_list->next;
+ }
+
+ asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+ AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+ rx_ctl |= 0x10;
+ }
+
+ asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static int ax88172_link_reset(struct usbnet *dev)
+{
+ u8 mode;
+ struct ethtool_cmd ecmd;
+
+ mii_check_media(&dev->mii, 1, 1);
+ mii_ethtool_gset(&dev->mii, &ecmd);
+ mode = AX88172_MEDIUM_DEFAULT;
+
+ if (ecmd.duplex != DUPLEX_FULL)
+ mode |= ~AX88172_MEDIUM_FD;
+
+ devdbg(dev, "ax88172_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+ asix_write_medium_mode(dev, mode);
+
+ return 0;
}
static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -501,6 +825,9 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
void *buf;
int i;
unsigned long gpio_bits = dev->driver_info->data;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
+ data->eeprom_len = AX88172_EEPROM_LEN;
usbnet_get_endpoints(dev,intf);
@@ -519,12 +846,12 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
msleep(5);
}
- if ((ret = asix_write_rx_ctl(dev,0x80)) < 0)
+ if ((ret = asix_write_rx_ctl(dev, 0x80)) < 0)
goto out2;
/* Get the MAC address */
memset(buf, 0, ETH_ALEN);
- if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
+ if ((ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
0, 0, 6, buf)) < 0) {
dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
goto out2;
@@ -537,14 +864,14 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
dev->mii.mdio_write = asix_mdio_write;
dev->mii.phy_id_mask = 0x3f;
dev->mii.reg_num_mask = 0x1f;
- dev->mii.phy_id = asix_get_phyid(dev);
+ dev->mii.phy_id = asix_get_phy_addr(dev);
dev->net->do_ioctl = asix_ioctl;
- dev->net->set_multicast_list = asix_set_multicast;
+ dev->net->set_multicast_list = ax88172_set_multicast;
dev->net->ethtool_ops = &ax88172_ethtool_ops;
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
mii_nway_restart(&dev->mii);
@@ -557,7 +884,8 @@ out1:
static struct ethtool_ops ax88772_ethtool_ops = {
.get_drvinfo = asix_get_drvinfo,
- .get_link = ethtool_op_get_link,
+ .get_link = asix_get_link,
+ .nway_reset = asix_nway_reset,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
.get_wol = asix_get_wol,
@@ -568,10 +896,37 @@ static struct ethtool_ops ax88772_ethtool_ops = {
.set_settings = asix_set_settings,
};
+static int ax88772_link_reset(struct usbnet *dev)
+{
+ u16 mode;
+ struct ethtool_cmd ecmd;
+
+ mii_check_media(&dev->mii, 1, 1);
+ mii_ethtool_gset(&dev->mii, &ecmd);
+ mode = AX88772_MEDIUM_DEFAULT;
+
+ if (ecmd.speed != SPEED_100)
+ mode &= ~AX_MEDIUM_PS;
+
+ if (ecmd.duplex != DUPLEX_FULL)
+ mode &= ~AX_MEDIUM_FD;
+
+ devdbg(dev, "ax88772_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+ asix_write_medium_mode(dev, mode);
+
+ return 0;
+}
+
static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
{
int ret;
void *buf;
+ u16 rx_ctl;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ u32 phyid;
+
+ data->eeprom_len = AX88772_EEPROM_LEN;
usbnet_get_endpoints(dev,intf);
@@ -582,13 +937,12 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
goto out1;
}
- if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
- 0x00B0, 0, 0, buf)) < 0)
+ if ((ret = asix_write_gpio(dev,
+ AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5)) < 0)
goto out2;
- msleep(5);
if ((ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
- 0x0001, 0, 0, buf)) < 0) {
+ 0x0000, 0, 0, buf)) < 0) {
dbg("Select PHY #1 failed: %d", ret);
goto out2;
}
@@ -605,36 +959,34 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
goto out2;
msleep(150);
- if ((ret = asix_write_rx_ctl(dev, 0x00)) < 0)
+ rx_ctl = asix_read_rx_ctl(dev);
+ dbg("RX_CTL is 0x%04x after software reset", rx_ctl);
+ if ((ret = asix_write_rx_ctl(dev, 0x0000)) < 0)
goto out2;
+ rx_ctl = asix_read_rx_ctl(dev);
+ dbg("RX_CTL is 0x%04x setting to 0x0000", rx_ctl);
+
/* Get the MAC address */
memset(buf, 0, ETH_ALEN);
- if ((ret = asix_read_cmd(dev, AX88772_CMD_READ_NODE_ID,
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
0, 0, ETH_ALEN, buf)) < 0) {
dbg("Failed to read MAC address: %d", ret);
goto out2;
}
memcpy(dev->net->dev_addr, buf, ETH_ALEN);
- if ((ret = asix_set_sw_mii(dev)) < 0)
- goto out2;
-
- if (((ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG,
- 0x0010, 2, 2, buf)) < 0)
- || (*((u16 *)buf) != 0x003b)) {
- dbg("Read PHY register 2 must be 0x3b00: %d", ret);
- goto out2;
- }
-
/* Initialize MII structure */
dev->mii.dev = dev->net;
dev->mii.mdio_read = asix_mdio_read;
dev->mii.mdio_write = asix_mdio_write;
- dev->mii.phy_id_mask = 0xff;
- dev->mii.reg_num_mask = 0xff;
+ dev->mii.phy_id_mask = 0x1f;
+ dev->mii.reg_num_mask = 0x1f;
dev->net->do_ioctl = asix_ioctl;
- dev->mii.phy_id = asix_get_phyid(dev);
+ dev->mii.phy_id = asix_get_phy_addr(dev);
+
+ phyid = asix_get_phyid(dev);
+ dbg("PHYID=0x%08x", phyid);
if ((ret = asix_sw_reset(dev, AX_SWRESET_PRL)) < 0)
goto out2;
@@ -649,16 +1001,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->set_multicast_list = asix_set_multicast;
dev->net->ethtool_ops = &ax88772_ethtool_ops;
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA);
mii_nway_restart(&dev->mii);
- if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
- AX88772_MEDIUM_DEFAULT, 0, 0, buf)) < 0) {
- dbg("Write medium mode register: %d", ret);
+ if ((ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT)) < 0)
goto out2;
- }
if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
@@ -666,13 +1015,17 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
goto out2;
}
- if ((ret = asix_set_hw_mii(dev)) < 0)
- goto out2;
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
- if ((ret = asix_write_rx_ctl(dev, 0x0088)) < 0)
+ if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
goto out2;
+ rx_ctl = asix_read_rx_ctl(dev);
+ dbg("RX_CTL is 0x%04x after all initializations", rx_ctl);
+
+ rx_ctl = asix_read_medium_status(dev);
+ dbg("Medium Status is 0x%04x after all initializations", rx_ctl);
+
kfree(buf);
/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
@@ -690,120 +1043,285 @@ out1:
return ret;
}
-static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+static struct ethtool_ops ax88178_ethtool_ops = {
+ .get_drvinfo = asix_get_drvinfo,
+ .get_link = asix_get_link,
+ .nway_reset = asix_nway_reset,
+ .get_msglevel = usbnet_get_msglevel,
+ .set_msglevel = usbnet_set_msglevel,
+ .get_wol = asix_get_wol,
+ .set_wol = asix_set_wol,
+ .get_eeprom_len = asix_get_eeprom_len,
+ .get_eeprom = asix_get_eeprom,
+ .get_settings = asix_get_settings,
+ .set_settings = asix_set_settings,
+};
+
+static int marvell_phy_init(struct usbnet *dev)
{
- u8 *head;
- u32 header;
- char *packet;
- struct sk_buff *ax_skb;
- u16 size;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ u16 reg;
- head = (u8 *) skb->data;
- memcpy(&header, head, sizeof(header));
- le32_to_cpus(&header);
- packet = head + sizeof(header);
+ devdbg(dev,"marvell_phy_init()");
- skb_pull(skb, 4);
+ reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS);
+ devdbg(dev,"MII_MARVELL_STATUS = 0x%04x", reg);
- while (skb->len > 0) {
- if ((short)(header & 0x0000ffff) !=
- ~((short)((header & 0xffff0000) >> 16))) {
- devdbg(dev,"header length data is error");
- }
- /* get the packet length */
- size = (u16) (header & 0x0000ffff);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL,
+ MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY);
- if ((skb->len) - ((size + 1) & 0xfffe) == 0)
- return 2;
- if (size > ETH_FRAME_LEN) {
- devdbg(dev,"invalid rx length %d", size);
- return 0;
- }
- ax_skb = skb_clone(skb, GFP_ATOMIC);
- if (ax_skb) {
- ax_skb->len = size;
- ax_skb->data = packet;
- ax_skb->tail = packet + size;
- usbnet_skb_return(dev, ax_skb);
- } else {
- return 0;
- }
+ if (data->ledmode) {
+ reg = asix_mdio_read(dev->net, dev->mii.phy_id,
+ MII_MARVELL_LED_CTRL);
+ devdbg(dev,"MII_MARVELL_LED_CTRL (1) = 0x%04x", reg);
- skb_pull(skb, (size + 1) & 0xfffe);
+ reg &= 0xf8ff;
+ reg |= (1 + 0x0100);
+ asix_mdio_write(dev->net, dev->mii.phy_id,
+ MII_MARVELL_LED_CTRL, reg);
- if (skb->len == 0)
- break;
+ reg = asix_mdio_read(dev->net, dev->mii.phy_id,
+ MII_MARVELL_LED_CTRL);
+ devdbg(dev,"MII_MARVELL_LED_CTRL (2) = 0x%04x", reg);
+ reg &= 0xfc0f;
+ }
- head = (u8 *) skb->data;
- memcpy(&header, head, sizeof(header));
- le32_to_cpus(&header);
- packet = head + sizeof(header);
- skb_pull(skb, 4);
+ return 0;
+}
+
+static int marvell_led_status(struct usbnet *dev, u16 speed)
+{
+ u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL);
+
+ devdbg(dev, "marvell_led_status() read 0x%04x", reg);
+
+ /* Clear out the center LED bits - 0x03F0 */
+ reg &= 0xfc0f;
+
+ switch (speed) {
+ case SPEED_1000:
+ reg |= 0x03e0;
+ break;
+ case SPEED_100:
+ reg |= 0x03b0;
+ break;
+ default:
+ reg |= 0x02f0;
}
- if (skb->len < 0) {
- devdbg(dev,"invalid rx length %d", skb->len);
- return 0;
+ devdbg(dev, "marvell_led_status() writing 0x%04x", reg);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg);
+
+ return 0;
+}
+
+static int ax88178_link_reset(struct usbnet *dev)
+{
+ u16 mode;
+ struct ethtool_cmd ecmd;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
+ devdbg(dev,"ax88178_link_reset()");
+
+ mii_check_media(&dev->mii, 1, 1);
+ mii_ethtool_gset(&dev->mii, &ecmd);
+ mode = AX88178_MEDIUM_DEFAULT;
+
+ if (ecmd.speed == SPEED_1000)
+ mode |= AX_MEDIUM_GM | AX_MEDIUM_ENCK;
+ else if (ecmd.speed == SPEED_100)
+ mode |= AX_MEDIUM_PS;
+ else
+ mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM);
+
+ if (ecmd.duplex == DUPLEX_FULL)
+ mode |= AX_MEDIUM_FD;
+ else
+ mode &= ~AX_MEDIUM_FD;
+
+ devdbg(dev, "ax88178_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+ asix_write_medium_mode(dev, mode);
+
+ if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
+ marvell_led_status(dev, ecmd.speed);
+
+ return 0;
+}
+
+static void ax88178_set_mfb(struct usbnet *dev)
+{
+ u16 mfb = AX_RX_CTL_MFB_16384;
+ u16 rxctl;
+ u16 medium;
+ int old_rx_urb_size = dev->rx_urb_size;
+
+ if (dev->hard_mtu < 2048) {
+ dev->rx_urb_size = 2048;
+ mfb = AX_RX_CTL_MFB_2048;
+ } else if (dev->hard_mtu < 4096) {
+ dev->rx_urb_size = 4096;
+ mfb = AX_RX_CTL_MFB_4096;
+ } else if (dev->hard_mtu < 8192) {
+ dev->rx_urb_size = 8192;
+ mfb = AX_RX_CTL_MFB_8192;
+ } else if (dev->hard_mtu < 16384) {
+ dev->rx_urb_size = 16384;
+ mfb = AX_RX_CTL_MFB_16384;
}
- return 1;
+
+ rxctl = asix_read_rx_ctl(dev);
+ asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb);
+
+ medium = asix_read_medium_status(dev);
+ if (dev->net->mtu > 1500)
+ medium |= AX_MEDIUM_JFE;
+ else
+ medium &= ~AX_MEDIUM_JFE;
+ asix_write_medium_mode(dev, medium);
+
+ if (dev->rx_urb_size > old_rx_urb_size)
+ usbnet_unlink_rx_urbs(dev);
}
-static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
- gfp_t flags)
+static int ax88178_change_mtu(struct net_device *net, int new_mtu)
{
- int padlen;
- int headroom = skb_headroom(skb);
- int tailroom = skb_tailroom(skb);
- u32 packet_len;
- u32 padbytes = 0xffff0000;
+ struct usbnet *dev = netdev_priv(net);
+ int ll_mtu = new_mtu + net->hard_header_len + 4;
- padlen = ((skb->len + 4) % 512) ? 0 : 4;
+ devdbg(dev, "ax88178_change_mtu() new_mtu=%d", new_mtu);
- if ((!skb_cloned(skb))
- && ((headroom + tailroom) >= (4 + padlen))) {
- if ((headroom < 4) || (tailroom < padlen)) {
- skb->data = memmove(skb->head + 4, skb->data, skb->len);
- skb->tail = skb->data + skb->len;
- }
+ if (new_mtu <= 0 || ll_mtu > 16384)
+ return -EINVAL;
+
+ if ((ll_mtu % dev->maxpacket) == 0)
+ return -EDOM;
+
+ net->mtu = new_mtu;
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+ ax88178_set_mfb(dev);
+
+ return 0;
+}
+
+static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ int ret;
+ void *buf;
+ u16 eeprom;
+ int gpio0 = 0;
+ u32 phyid;
+
+ usbnet_get_endpoints(dev,intf);
+
+ buf = kmalloc(6, GFP_KERNEL);
+ if(!buf) {
+ dbg ("Cannot allocate memory for buffer");
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ eeprom = 0;
+ asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &eeprom);
+ dbg("GPIO Status: 0x%04x", eeprom);
+
+ asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
+ asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
+ asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
+
+ dbg("EEPROM index 0x17 is 0x%04x", eeprom);
+
+ if (eeprom == 0xffff) {
+ data->phymode = PHY_MODE_MARVELL;
+ data->ledmode = 0;
+ gpio0 = 1;
} else {
- struct sk_buff *skb2;
- skb2 = skb_copy_expand(skb, 4, padlen, flags);
- dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ data->phymode = eeprom & 7;
+ data->ledmode = eeprom >> 8;
+ gpio0 = (eeprom & 0x80) ? 0 : 1;
}
+ dbg("GPIO0: %d, PhyMode: %d", gpio0, data->phymode);
- skb_push(skb, 4);
- packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
- memcpy(skb->data, &packet_len, sizeof(packet_len));
+ asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
+ if ((eeprom >> 8) != 1) {
+ asix_write_gpio(dev, 0x003c, 30);
+ asix_write_gpio(dev, 0x001c, 300);
+ asix_write_gpio(dev, 0x003c, 30);
+ } else {
+ dbg("gpio phymode == 1 path");
+ asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
+ asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
+ }
- if ((skb->len % 512) == 0) {
- memcpy( skb->tail, &padbytes, sizeof(padbytes));
- skb_put(skb, sizeof(padbytes));
+ asix_sw_reset(dev, 0);
+ msleep(150);
+
+ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
+ msleep(150);
+
+ asix_write_rx_ctl(dev, 0);
+
+ /* Get the MAC address */
+ memset(buf, 0, ETH_ALEN);
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
+ 0, 0, ETH_ALEN, buf)) < 0) {
+ dbg("Failed to read MAC address: %d", ret);
+ goto out2;
}
- return skb;
-}
+ memcpy(dev->net->dev_addr, buf, ETH_ALEN);
-static int ax88772_link_reset(struct usbnet *dev)
-{
- u16 lpa;
- u16 adv;
- u16 res;
- u16 mode;
+ /* Initialize MII structure */
+ dev->mii.dev = dev->net;
+ dev->mii.mdio_read = asix_mdio_read;
+ dev->mii.mdio_write = asix_mdio_write;
+ dev->mii.phy_id_mask = 0x1f;
+ dev->mii.reg_num_mask = 0xff;
+ dev->mii.supports_gmii = 1;
+ dev->net->do_ioctl = asix_ioctl;
+ dev->mii.phy_id = asix_get_phy_addr(dev);
+ dev->net->set_multicast_list = asix_set_multicast;
+ dev->net->ethtool_ops = &ax88178_ethtool_ops;
+ dev->net->change_mtu = &ax88178_change_mtu;
- mode = AX88772_MEDIUM_DEFAULT;
- lpa = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
- adv = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
- res = mii_nway_result(lpa|adv);
+ phyid = asix_get_phyid(dev);
+ dbg("PHYID=0x%08x", phyid);
+
+ if (data->phymode == PHY_MODE_MARVELL) {
+ marvell_phy_init(dev);
+ msleep(60);
+ }
+
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+ BMCR_RESET | BMCR_ANENABLE);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
+ ADVERTISE_1000FULL);
+
+ mii_nway_restart(&dev->mii);
+
+ if ((ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT)) < 0)
+ goto out2;
- if ((res & LPA_DUPLEX) == 0)
- mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
- if ((res & LPA_100) == 0)
- mode &= ~AX88772_MEDIUM_100MB;
- asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+ if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
+ goto out2;
+
+ kfree(buf);
+
+ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+ if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+ /* hard_mtu is still the default - the device does not support
+ jumbo eth frames */
+ dev->rx_urb_size = 2048;
+ }
return 0;
+
+out2:
+ kfree(buf);
+out1:
+ return ret;
}
static const struct driver_info ax8817x_info = {
@@ -853,8 +1371,19 @@ static const struct driver_info ax88772_info = {
.link_reset = ax88772_link_reset,
.reset = ax88772_link_reset,
.flags = FLAG_ETHER | FLAG_FRAMING_AX,
- .rx_fixup = ax88772_rx_fixup,
- .tx_fixup = ax88772_tx_fixup,
+ .rx_fixup = asix_rx_fixup,
+ .tx_fixup = asix_tx_fixup,
+};
+
+static const struct driver_info ax88178_info = {
+ .description = "ASIX AX88178 USB 2.0 Ethernet",
+ .bind = ax88178_bind,
+ .status = asix_status,
+ .link_reset = ax88178_link_reset,
+ .reset = ax88178_link_reset,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+ .rx_fixup = asix_rx_fixup,
+ .tx_fixup = asix_tx_fixup,
};
static const struct usb_device_id products [] = {
@@ -913,7 +1442,7 @@ static const struct usb_device_id products [] = {
}, {
// ASIX AX88178 10/100/1000
USB_DEVICE (0x0b95, 0x1780),
- .driver_info = (unsigned long) &ax88772_info,
+ .driver_info = (unsigned long) &ax88178_info,
}, {
// Linksys USB200M Rev 2
USB_DEVICE (0x13b1, 0x0018),
@@ -922,6 +1451,18 @@ static const struct usb_device_id products [] = {
// 0Q0 cable ethernet
USB_DEVICE (0x1557, 0x7720),
.driver_info = (unsigned long) &ax88772_info,
+}, {
+ // DLink DUB-E100 H/W Ver B1
+ USB_DEVICE (0x07d1, 0x3c05),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
+ // DLink DUB-E100 H/W Ver B1 Alternate
+ USB_DEVICE (0x2001, 0x3c05),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
+ // Linksys USB1000
+ USB_DEVICE (0x1737, 0x0039),
+ .driver_info = (unsigned long) &ax88178_info,
},
{ }, // END
};
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index def3bb8e229..544d41fe9b9 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -165,6 +165,7 @@ static struct usb_device_id usb_klsi_table[] = {
{ USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */
{ USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */
{ USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */
+ { USB_DEVICE(0x1668, 0x0323) }, /* Actiontec USB Ethernet */
{ USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */
{} /* Null terminator */
};
diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c
index a9b6eeac3e3..301baa72bac 100644
--- a/drivers/usb/net/net1080.c
+++ b/drivers/usb/net/net1080.c
@@ -498,25 +498,24 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
static struct sk_buff *
net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
{
- int padlen;
struct sk_buff *skb2;
struct nc_header *header = NULL;
struct nc_trailer *trailer = NULL;
+ int padlen = sizeof (struct nc_trailer);
int len = skb->len;
- padlen = ((len + sizeof (struct nc_header)
- + sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
+ if (!((len + padlen + sizeof (struct nc_header)) & 0x01))
+ padlen++;
if (!skb_cloned(skb)) {
int headroom = skb_headroom(skb);
int tailroom = skb_tailroom(skb);
- if ((padlen + sizeof (struct nc_trailer)) <= tailroom
- && sizeof (struct nc_header) <= headroom)
+ if (padlen <= tailroom &&
+ sizeof(struct nc_header) <= headroom)
/* There's enough head and tail room */
goto encapsulate;
- if ((sizeof (struct nc_header) + padlen
- + sizeof (struct nc_trailer)) <
+ if ((sizeof (struct nc_header) + padlen) <
(headroom + tailroom)) {
/* There's enough total room, so just readjust */
skb->data = memmove(skb->head
@@ -530,7 +529,7 @@ net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
/* Create a new skb to use with the correct size */
skb2 = skb_copy_expand(skb,
sizeof (struct nc_header),
- sizeof (struct nc_trailer) + padlen,
+ padlen,
flags);
dev_kfree_skb_any(skb);
if (!skb2)
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index ab21f960d25..918cf5a77c0 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -45,7 +45,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.6.13 (2005/11/13)"
+#define DRIVER_VERSION "v0.6.14 (2006/09/27)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
@@ -339,7 +339,7 @@ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
}
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return ret;
}
@@ -376,7 +376,7 @@ static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd)
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return -ETIMEDOUT;
}
@@ -413,7 +413,7 @@ static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return -ETIMEDOUT;
}
@@ -461,7 +461,7 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
return ret;
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return -ETIMEDOUT;
}
#endif /* PEGASUS_WRITE_EEPROM */
@@ -481,8 +481,12 @@ static void set_ethernet_addr(pegasus_t * pegasus)
{
__u8 node_id[6];
- get_node_id(pegasus, node_id);
- set_registers(pegasus, EthID, sizeof (node_id), node_id);
+ if (pegasus->features & PEGASUS_II) {
+ get_registers(pegasus, 0x10, sizeof(node_id), node_id);
+ } else {
+ get_node_id(pegasus, node_id);
+ set_registers(pegasus, EthID, sizeof (node_id), node_id);
+ }
memcpy(pegasus->net->dev_addr, node_id, sizeof (node_id));
}
@@ -619,7 +623,7 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
switch (urb->status) {
case 0:
break;
- case -ETIMEDOUT:
+ case -ETIME:
if (netif_msg_rx_err(pegasus))
pr_debug("%s: reset MAC\n", net->name);
pegasus->flags &= ~PEGASUS_RX_BUSY;
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index a72685b9606..2364c209938 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -438,7 +438,7 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
break;
case -ENOENT:
return; /* the urb is in unlink state */
- case -ETIMEDOUT:
+ case -ETIME:
warn("may be reset is needed?..");
goto goon;
default:
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
index 54183e173a6..98a522f1e26 100644
--- a/drivers/usb/net/usbnet.c
+++ b/drivers/usb/net/usbnet.c
@@ -61,8 +61,11 @@
* let the USB host controller be busy for 5msec or more before an irq
* is required, under load. Jumbograms change the equation.
*/
-#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
-#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#define RX_MAX_QUEUE_MEMORY (60 * 1518)
+#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+ (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)
+#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+ (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)
// reawaken network queue this soon after stopping; else watchdog barks
#define TX_TIMEOUT_JIFFIES (5*HZ)
@@ -227,13 +230,23 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu)
{
struct usbnet *dev = netdev_priv(net);
int ll_mtu = new_mtu + net->hard_header_len;
+ int old_hard_mtu = dev->hard_mtu;
+ int old_rx_urb_size = dev->rx_urb_size;
- if (new_mtu <= 0 || ll_mtu > dev->hard_mtu)
+ if (new_mtu <= 0)
return -EINVAL;
// no second zero-length packet read wanted after mtu-sized packets
if ((ll_mtu % dev->maxpacket) == 0)
return -EDOM;
net->mtu = new_mtu;
+
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+ if (dev->rx_urb_size == old_hard_mtu) {
+ dev->rx_urb_size = dev->hard_mtu;
+ if (dev->rx_urb_size > old_rx_urb_size)
+ usbnet_unlink_rx_urbs(dev);
+ }
+
return 0;
}
@@ -412,9 +425,9 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
// we get controller i/o faults during khubd disconnect() delays.
// throttle down resubmits, to avoid log floods; just temporarily,
// so we still recover when the fault isn't a khubd delay.
- case -EPROTO: // ehci
- case -ETIMEDOUT: // ohci
- case -EILSEQ: // uhci
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
dev->stats.rx_errors++;
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
@@ -521,6 +534,17 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
return count;
}
+// Flush all pending rx urbs
+// minidrivers may need to do this when the MTU changes
+
+void usbnet_unlink_rx_urbs(struct usbnet *dev)
+{
+ if (netif_running(dev->net)) {
+ (void) unlink_urbs (dev, &dev->rxq);
+ tasklet_schedule(&dev->bh);
+ }
+}
+EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
/*-------------------------------------------------------------------------*/
@@ -629,7 +653,7 @@ static int usbnet_open (struct net_device *net)
devinfo (dev, "open: enable queueing "
"(rx %d, tx %d) mtu %d %s framing",
- RX_QLEN (dev), TX_QLEN (dev), dev->net->mtu,
+ (int)RX_QLEN (dev), (int)TX_QLEN (dev), dev->net->mtu,
framing);
}
@@ -797,9 +821,9 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs)
// like rx, tx gets controller i/o faults during khubd delays
// and so it uses the same throttling mechanism.
- case -EPROTO: // ehci
- case -ETIMEDOUT: // ohci
- case -EILSEQ: // uhci
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay,
jiffies + THROTTLE_JIFFIES);
diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h
index 89fc4958eec..c0746f0454a 100644
--- a/drivers/usb/net/usbnet.h
+++ b/drivers/usb/net/usbnet.h
@@ -166,6 +166,7 @@ struct skb_data { /* skb->cb is one of these */
extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *);
extern void usbnet_defer_kevent (struct usbnet *, int);
extern void usbnet_skb_return (struct usbnet *, struct sk_buff *);
+extern void usbnet_unlink_rx_urbs(struct usbnet *);
extern u32 usbnet_get_msglevel (struct net_device *);
extern void usbnet_set_msglevel (struct net_device *, u32);
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index f5b9438c94f..5076b9d9705 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -53,6 +53,15 @@ config USB_SERIAL_GENERIC
support" be compiled as a module for this driver to be used
properly.
+config USB_SERIAL_AIRCABLE
+ tristate "AIRcable USB Bluetooth Dongle Driver (EXPERIMENTAL)"
+ depends on USB_SERIAL && EXPERIMENTAL
+ help
+ Say Y here if you want to use AIRcable USB Bluetoot Dongle.
+
+ To compile this driver as a module, choose M here: the module
+ will be called aircable.
+
config USB_SERIAL_AIRPRIME
tristate "USB AirPrime CDMA Wireless Driver"
depends on USB_SERIAL
@@ -413,6 +422,21 @@ config USB_SERIAL_MCT_U232
To compile this driver as a module, choose M here: the
module will be called mct_u232.
+config USB_SERIAL_MOS7840
+ tristate "USB Moschip 7840/7820 USB Serial Driver"
+ depends on USB_SERIAL
+ ---help---
+ Say Y here if you want to use a MCS7840 Quad-Serial or MCS7820
+ Dual-Serial port device from MosChip Semiconductor.
+
+ The MCS7840 and MCS7820 have been developed to connect a wide range
+ of standard serial devices to a USB host. The MCS7840 has a USB
+ device controller connected to four (4) individual UARTs while the
+ MCS7820 controller connects to two (2) individual UARTs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mos7840. If unsure, choose N.
+
config USB_SERIAL_NAVMAN
tristate "USB Navman GPS device"
depends on USB_SERIAL
@@ -526,5 +550,6 @@ config USB_EZUSB
depends on USB_SERIAL_KEYSPAN_PDA || USB_SERIAL_XIRCOM || USB_SERIAL_KEYSPAN || USB_SERIAL_WHITEHEAT
default y
+
endmenu
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 8efed2ce1ba..8dce83340e3 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -11,6 +11,7 @@ usbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.o
usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y)
+obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o
obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.o
obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o
obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o
@@ -33,6 +34,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o
obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o
+obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
new file mode 100644
index 00000000000..2ccd9ded52a
--- /dev/null
+++ b/drivers/usb/serial/aircable.c
@@ -0,0 +1,625 @@
+/*
+ * AIRcable USB Bluetooth Dongle Driver.
+ *
+ * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com)
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * The device works as an standard CDC device, it has 2 interfaces, the first
+ * one is for firmware access and the second is the serial one.
+ * The protocol is very simply, there are two posibilities reading or writing.
+ * When writting the first urb must have a Header that starts with 0x20 0x29 the
+ * next two bytes must say how much data will be sended.
+ * When reading the process is almost equal except that the header starts with
+ * 0x00 0x20.
+ *
+ * The device simply need some stuff to understand data comming from the usb
+ * buffer: The First and Second byte is used for a Header, the Third and Fourth
+ * tells the device the amount of information the package holds.
+ * Packages are 60 bytes long Header Stuff.
+ * When writting to the device the first two bytes of the header are 0x20 0x29
+ * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange
+ * situation, when too much data arrives to the device because it sends the data
+ * but with out the header. I will use a simply hack to override this situation,
+ * if there is data coming that does not contain any header, then that is data
+ * that must go directly to the tty, as there is no documentation about if there
+ * is any other control code, I will simply check for the first
+ * one.
+ *
+ * The driver registers himself with the USB-serial core and the USB Core. I had
+ * to implement a probe function agains USB-serial, because other way, the
+ * driver was attaching himself to both interfaces. I have tryed with different
+ * configurations of usb_serial_driver with out exit, only the probe function
+ * could handle this correctly.
+ *
+ * I have taken some info from a Greg Kroah-Hartman article:
+ * http://www.linuxjournal.com/article/6573
+ * And from Linux Device Driver Kit CD, which is a great work, the authors taken
+ * the work to recompile lots of information an knowladge in drivers development
+ * and made it all avaible inside a cd.
+ * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/
+ *
+ */
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/circ_buf.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static int debug;
+
+/* Vendor and Product ID */
+#define AIRCABLE_VID 0x16CA
+#define AIRCABLE_USB_PID 0x1502
+
+/* write buffer size defines */
+#define AIRCABLE_BUF_SIZE 2048
+
+/* Protocol Stuff */
+#define HCI_HEADER_LENGTH 0x4
+#define TX_HEADER_0 0x20
+#define TX_HEADER_1 0x29
+#define RX_HEADER_0 0x00
+#define RX_HEADER_1 0x20
+#define MAX_HCI_FRAMESIZE 60
+#define HCI_COMPLETE_FRAME 64
+
+/* rx_flags */
+#define THROTTLED 0x01
+#define ACTUALLY_THROTTLED 0x02
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0b2"
+#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>"
+#define DRIVER_DESC "AIRcable USB Driver"
+
+/* ID table that will be registered with USB core */
+static struct usb_device_id id_table [] = {
+ { USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+/* Internal Structure */
+struct aircable_private {
+ spinlock_t rx_lock; /* spinlock for the receive lines */
+ struct circ_buf *tx_buf; /* write buffer */
+ struct circ_buf *rx_buf; /* read buffer */
+ int rx_flags; /* for throttilng */
+ struct work_struct rx_work; /* work cue for the receiving line */
+};
+
+/* Private methods */
+
+/* Circular Buffer Methods, code from ti_usb_3410_5052 used */
+/*
+ * serial_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void serial_buf_clear(struct circ_buf *cb)
+{
+ cb->head = cb->tail = 0;
+}
+
+/*
+ * serial_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static struct circ_buf *serial_buf_alloc(void)
+{
+ struct circ_buf *cb;
+ cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
+ if (cb == NULL)
+ return NULL;
+ cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL);
+ if (cb->buf == NULL) {
+ kfree(cb);
+ return NULL;
+ }
+ serial_buf_clear(cb);
+ return cb;
+}
+
+/*
+ * serial_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void serial_buf_free(struct circ_buf *cb)
+{
+ kfree(cb->buf);
+ kfree(cb);
+}
+
+/*
+ * serial_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static int serial_buf_data_avail(struct circ_buf *cb)
+{
+ return CIRC_CNT(cb->head,cb->tail,AIRCABLE_BUF_SIZE);
+}
+
+/*
+ * serial_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static int serial_buf_put(struct circ_buf *cb, const char *buf, int count)
+{
+ int c, ret = 0;
+ while (1) {
+ c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+ memcpy(cb->buf + cb->head, buf, c);
+ cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1);
+ buf += c;
+ count -= c;
+ ret= c;
+ }
+ return ret;
+}
+
+/*
+ * serial_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static int serial_buf_get(struct circ_buf *cb, char *buf, int count)
+{
+ int c, ret = 0;
+ while (1) {
+ c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+ memcpy(buf, cb->buf + cb->tail, c);
+ cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1);
+ buf += c;
+ count -= c;
+ ret= c;
+ }
+ return ret;
+}
+
+/* End of circula buffer methods */
+
+static void aircable_send(struct usb_serial_port *port)
+{
+ int count, result;
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ unsigned char* buf;
+ dbg("%s - port %d", __FUNCTION__, port->number);
+ if (port->write_urb_busy)
+ return;
+
+ count = min(serial_buf_data_avail(priv->tx_buf), MAX_HCI_FRAMESIZE);
+ if (count == 0)
+ return;
+
+ buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC);
+ if (!buf) {
+ err("%s- kzalloc(%d) failed.", __FUNCTION__,
+ count + HCI_HEADER_LENGTH);
+ return;
+ }
+
+ buf[0] = TX_HEADER_0;
+ buf[1] = TX_HEADER_1;
+ buf[2] = (unsigned char)count;
+ buf[3] = (unsigned char)(count >> 8);
+ serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE);
+
+ memcpy(port->write_urb->transfer_buffer, buf,
+ count + HCI_HEADER_LENGTH);
+
+ kfree(buf);
+ port->write_urb_busy = 1;
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ count + HCI_HEADER_LENGTH,
+ port->write_urb->transfer_buffer);
+ port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH;
+ port->write_urb->dev = port->serial->dev;
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+
+ if (result) {
+ dev_err(&port->dev,
+ "%s - failed submitting write urb, error %d\n",
+ __FUNCTION__, result);
+ port->write_urb_busy = 0;
+ }
+
+ schedule_work(&port->work);
+}
+
+static void aircable_read(void *params)
+{
+ struct usb_serial_port *port = params;
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned char *data;
+ int count;
+ if (priv->rx_flags & THROTTLED){
+ if (priv->rx_flags & ACTUALLY_THROTTLED)
+ schedule_work(&priv->rx_work);
+ return;
+ }
+
+ /* By now I will flush data to the tty in packages of no more than
+ * 64 bytes, to ensure I do not get throttled.
+ * Ask USB mailing list for better aproach.
+ */
+ tty = port->tty;
+
+ if (!tty)
+ schedule_work(&priv->rx_work);
+
+ count = min(64, serial_buf_data_avail(priv->rx_buf));
+
+ if (count <= 0)
+ return; //We have finished sending everything.
+
+ tty_prepare_flip_string(tty, &data, count);
+ if (!data){
+ err("%s- kzalloc(%d) failed.", __FUNCTION__, count);
+ return;
+ }
+
+ serial_buf_get(priv->rx_buf, data, count);
+
+ tty_flip_buffer_push(tty);
+
+ if (serial_buf_data_avail(priv->rx_buf))
+ schedule_work(&priv->rx_work);
+
+ return;
+}
+/* End of private methods */
+
+static int aircable_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *iface_desc = serial->interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint;
+ int num_bulk_out=0;
+ int i;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+ ((endpoint->bmAttributes & 3) == 0x02)) {
+ /* we found our bulk out endpoint */
+ dbg("found bulk out on endpoint %d", i);
+ ++num_bulk_out;
+ }
+ }
+
+ if (num_bulk_out == 0) {
+ dbg("Invalid interface, discarding");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int aircable_attach (struct usb_serial *serial)
+{
+ struct usb_serial_port *port = serial->port[0];
+ struct aircable_private *priv;
+
+ priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL);
+ if (!priv){
+ err("%s- kmalloc(%Zd) failed.", __FUNCTION__,
+ sizeof(struct aircable_private));
+ return -ENOMEM;
+ }
+
+ /* Allocation of Circular Buffers */
+ priv->tx_buf = serial_buf_alloc();
+ if (priv->tx_buf == NULL) {
+ kfree(priv);
+ return -ENOMEM;
+ }
+
+ priv->rx_buf = serial_buf_alloc();
+ if (priv->rx_buf == NULL) {
+ kfree(priv->tx_buf);
+ kfree(priv);
+ return -ENOMEM;
+ }
+
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ INIT_WORK(&priv->rx_work, aircable_read, port);
+
+ usb_set_serial_port_data(serial->port[0], priv);
+
+ return 0;
+}
+
+static void aircable_shutdown(struct usb_serial *serial)
+{
+
+ struct usb_serial_port *port = serial->port[0];
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+
+ dbg("%s", __FUNCTION__);
+
+ if (priv) {
+ serial_buf_free(priv->tx_buf);
+ serial_buf_free(priv->rx_buf);
+ usb_set_serial_port_data(port, NULL);
+ kfree(priv);
+ }
+}
+
+static int aircable_write_room(struct usb_serial_port *port)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ return serial_buf_data_avail(priv->tx_buf);
+}
+
+static int aircable_write(struct usb_serial_port *port,
+ const unsigned char *source, int count)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ int temp;
+
+ dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, source);
+
+ if (!count){
+ dbg("%s - write request of 0 bytes", __FUNCTION__);
+ return count;
+ }
+
+ temp = serial_buf_put(priv->tx_buf, source, count);
+
+ aircable_send(port);
+
+ if (count > AIRCABLE_BUF_SIZE)
+ count = AIRCABLE_BUF_SIZE;
+
+ return count;
+
+}
+
+static void aircable_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ int result;
+
+ dbg("%s - urb->status: %d", __FUNCTION__ , urb->status);
+
+ /* This has been taken from cypress_m8.c cypress_write_int_callback */
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ port->write_urb_busy = 0;
+ return;
+ default:
+ /* error in the urb, so we have to resubmit it */
+ dbg("%s - Overflow in write", __FUNCTION__);
+ dbg("%s - nonzero write bulk status received: %d",
+ __FUNCTION__, urb->status);
+ port->write_urb->transfer_buffer_length = 1;
+ port->write_urb->dev = port->serial->dev;
+ result = usb_submit_urb(port->write_urb, GFP_KERNEL);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting write urb, error %d\n",
+ __FUNCTION__, result);
+ else
+ return;
+ }
+
+ port->write_urb_busy = 0;
+
+ aircable_send(port);
+}
+
+static void aircable_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned long no_packages, remaining, package_length, i;
+ int result, shift = 0;
+ unsigned char *temp;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (urb->status) {
+ dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
+ if (!port->open_count) {
+ dbg("%s - port is closed, exiting.", __FUNCTION__);
+ return;
+ }
+ if (urb->status == -EPROTO) {
+ dbg("%s - caught -EPROTO, resubmitting the urb",
+ __FUNCTION__);
+ usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ aircable_read_bulk_callback, port);
+
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
+ return;
+ }
+ dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
+ return;
+ }
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ urb->actual_length,urb->transfer_buffer);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ if (urb->actual_length <= 2) {
+ /* This is an incomplete package */
+ serial_buf_put(priv->rx_buf, urb->transfer_buffer,
+ urb->actual_length);
+ } else {
+ temp = urb->transfer_buffer;
+ if (temp[0] == RX_HEADER_0)
+ shift = HCI_HEADER_LENGTH;
+
+ remaining = urb->actual_length;
+ no_packages = urb->actual_length / (HCI_COMPLETE_FRAME);
+
+ if (urb->actual_length % HCI_COMPLETE_FRAME != 0)
+ no_packages+=1;
+
+ for (i = 0; i < no_packages ;i++) {
+ if (remaining > (HCI_COMPLETE_FRAME))
+ package_length = HCI_COMPLETE_FRAME;
+ else
+ package_length = remaining;
+ remaining -= package_length;
+
+ serial_buf_put(priv->rx_buf,
+ urb->transfer_buffer + shift +
+ (HCI_COMPLETE_FRAME) * (i),
+ package_length - shift);
+ }
+ }
+ aircable_read(port);
+ }
+
+ /* Schedule the next read _if_ we are still open */
+ if (port->open_count) {
+ usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ aircable_read_bulk_callback, port);
+
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
+ }
+
+ return;
+}
+
+/* Based on ftdi_sio.c throttle */
+static void aircable_throttle(struct usb_serial_port *port)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ priv->rx_flags |= THROTTLED;
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+}
+
+/* Based on ftdi_sio.c unthrottle */
+static void aircable_unthrottle(struct usb_serial_port *port)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ int actually_throttled;
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+
+ if (actually_throttled)
+ schedule_work(&priv->rx_work);
+}
+
+static struct usb_serial_driver aircable_device = {
+ .description = "aircable",
+ .id_table = id_table,
+ .num_ports = 1,
+ .attach = aircable_attach,
+ .probe = aircable_probe,
+ .shutdown = aircable_shutdown,
+ .write = aircable_write,
+ .write_room = aircable_write_room,
+ .write_bulk_callback = aircable_write_bulk_callback,
+ .read_bulk_callback = aircable_read_bulk_callback,
+ .throttle = aircable_throttle,
+ .unthrottle = aircable_unthrottle,
+};
+
+static struct usb_driver aircable_driver = {
+ .name = "aircable",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = id_table,
+};
+
+static int __init aircable_init (void)
+{
+ int retval;
+ retval = usb_serial_register(&aircable_device);
+ if (retval)
+ goto failed_serial_register;
+ retval = usb_register(&aircable_driver);
+ if (retval)
+ goto failed_usb_register;
+ return 0;
+
+failed_serial_register:
+ usb_serial_deregister(&aircable_device);
+failed_usb_register:
+ return retval;
+}
+
+static void __exit aircable_exit (void)
+{
+ usb_deregister(&aircable_driver);
+ usb_serial_deregister(&aircable_device);
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_init(aircable_init);
+module_exit(aircable_exit);
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c
index 62082532a8b..6e1a84a858d 100644
--- a/drivers/usb/serial/airprime.c
+++ b/drivers/usb/serial/airprime.c
@@ -1,7 +1,7 @@
/*
* AirPrime CDMA Wireless Serial USB driver
*
- * Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2005-2006 Greg Kroah-Hartman <gregkh@suse.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
@@ -11,26 +11,264 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
- { USB_DEVICE(0xf3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */
- { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
+ { USB_DEVICE(0x0f3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */
+ { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
+ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless Aircard 580 */
{ USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */
+ { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
+#define URB_TRANSFER_BUFFER_SIZE 4096
+#define NUM_READ_URBS 4
+#define NUM_WRITE_URBS 4
+#define NUM_BULK_EPS 3
+#define MAX_BULK_EPS 6
+
+/* if overridden by the user, then use their value for the size of the
+ * read and write urbs, and the number of endpoints */
+static int buffer_size = URB_TRANSFER_BUFFER_SIZE;
+static int endpoints = NUM_BULK_EPS;
+static int debug;
+struct airprime_private {
+ spinlock_t lock;
+ int outstanding_urbs;
+ int throttled;
+ struct urb *read_urbp[NUM_READ_URBS];
+};
+
+static void airprime_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ struct tty_struct *tty;
+ int result;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (urb->status) {
+ dbg("%s - nonzero read bulk status received: %d",
+ __FUNCTION__, urb->status);
+ /* something happened, so free up the memory for this urb */
+ if (urb->transfer_buffer) {
+ kfree (urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+ return;
+ }
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ tty_insert_flip_string (tty, data, urb->actual_length);
+ tty_flip_buffer_push (tty);
+ }
+
+ result = usb_submit_urb (urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
+ return;
+}
+
+static void airprime_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* free up the transfer buffer, as usb_free_urb() does not do this */
+ kfree (urb->transfer_buffer);
+
+ if (urb->status)
+ dbg("%s - nonzero write bulk status received: %d",
+ __FUNCTION__, urb->status);
+ spin_lock_irqsave(&priv->lock, flags);
+ --priv->outstanding_urbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ usb_serial_port_softint(port);
+}
+
+static int airprime_open(struct usb_serial_port *port, struct file *filp)
+{
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ struct urb *urb;
+ char *buffer = NULL;
+ int i;
+ int result = 0;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* initialize our private data structure if it isn't already created */
+ if (!priv) {
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ result = -ENOMEM;
+ goto out;
+ }
+ spin_lock_init(&priv->lock);
+ usb_set_serial_port_data(port, priv);
+ }
+
+ for (i = 0; i < NUM_READ_URBS; ++i) {
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&port->dev, "%s - out of memory.\n",
+ __FUNCTION__);
+ result = -ENOMEM;
+ goto errout;
+ }
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&port->dev, "%s - no more urbs?\n",
+ __FUNCTION__);
+ result = -ENOMEM;
+ goto errout;
+ }
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ buffer, buffer_size,
+ airprime_read_bulk_callback, port);
+ result = usb_submit_urb(urb, GFP_KERNEL);
+ if (result) {
+ dev_err(&port->dev,
+ "%s - failed submitting read urb %d for port %d, error %d\n",
+ __FUNCTION__, i, port->number, result);
+ goto errout;
+ }
+ /* remember this urb so we can kill it when the port is closed */
+ priv->read_urbp[i] = urb;
+ }
+ goto out;
+
+ errout:
+ /* some error happened, cancel any submitted urbs and clean up anything that
+ got allocated successfully */
+
+ for ( ; i >= 0; --i) {
+ urb = priv->read_urbp[i];
+ if (urb) {
+ /* This urb was submitted successfully. So we have to
+ cancel it.
+ Unlinking the urb will invoke read_bulk_callback()
+ with an error status, so its transfer buffer will
+ be freed there */
+ if (usb_unlink_urb (urb) != -EINPROGRESS) {
+ /* comments in drivers/usb/core/urb.c say this
+ can only happen if the urb was never submitted,
+ or has completed already.
+ Either way we may have to free the transfer
+ buffer here. */
+ if (urb->transfer_buffer) {
+ kfree (urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+ }
+ usb_free_urb (urb);
+ }
+ }
+
+ out:
+ return result;
+}
+
+static void airprime_close(struct usb_serial_port *port, struct file * filp)
+{
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ int i;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* killing the urb will invoke read_bulk_callback() with an error status,
+ so the transfer buffer will be freed there */
+ for (i = 0; i < NUM_READ_URBS; ++i) {
+ usb_kill_urb (priv->read_urbp[i]);
+ usb_free_urb (priv->read_urbp[i]);
+ }
+
+ /* free up private structure */
+ kfree (priv);
+ usb_set_serial_port_data(port, NULL);
+}
+
+static int airprime_write(struct usb_serial_port *port,
+ const unsigned char *buf, int count)
+{
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ struct urb *urb;
+ unsigned char *buffer;
+ unsigned long flags;
+ int status;
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->outstanding_urbs > NUM_WRITE_URBS) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dbg("%s - write limit hit\n", __FUNCTION__);
+ return 0;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ buffer = kmalloc(count, GFP_ATOMIC);
+ if (!buffer) {
+ dev_err(&port->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ dev_err(&port->dev, "no more free urbs\n");
+ kfree (buffer);
+ return -ENOMEM;
+ }
+ memcpy (buffer, buf, count);
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer);
+
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ buffer, count,
+ airprime_write_bulk_callback, port);
+
+ /* send it down the pipe */
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
+ dev_err(&port->dev,
+ "%s - usb_submit_urb(write bulk) failed with status = %d\n",
+ __FUNCTION__, status);
+ count = status;
+ kfree (buffer);
+ } else {
+ spin_lock_irqsave(&priv->lock, flags);
+ ++priv->outstanding_urbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ /* we are done with this urb, so let the host driver
+ * really free it when it is finished with it */
+ usb_free_urb (urb);
+ return count;
+}
+
static struct usb_driver airprime_driver = {
.name = "airprime",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
- .no_dynamic_id = 1,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver airprime_device = {
@@ -42,13 +280,17 @@ static struct usb_serial_driver airprime_device = {
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE,
- .num_ports = 1,
+ .open = airprime_open,
+ .close = airprime_close,
+ .write = airprime_write,
};
static int __init airprime_init(void)
{
int retval;
+ airprime_device.num_ports =
+ (endpoints > 0 && endpoints <= MAX_BULK_EPS) ? endpoints : NUM_BULK_EPS;
retval = usb_serial_register(&airprime_device);
if (retval)
return retval;
@@ -60,6 +302,8 @@ static int __init airprime_init(void)
static void __exit airprime_exit(void)
{
+ dbg("%s", __FUNCTION__);
+
usb_deregister(&airprime_driver);
usb_serial_deregister(&airprime_device);
}
@@ -67,3 +311,10 @@ static void __exit airprime_exit(void)
module_init(airprime_init);
module_exit(airprime_exit);
MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled");
+module_param(buffer_size, int, 0);
+MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers in bytes (default 4096)");
+module_param(endpoints, int, 0);
+MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)");
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 970d9ef0a7a..ca52f12f0e2 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2006
+ * Simon Schulz (ark3116_driver <at> auctionant.de)
+ *
* ark3116
* - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547,
* productid=0x0232) (used in a datacable called KQ-U8A)
@@ -8,8 +11,6 @@
*
* - based on logs created by usbsnoopy
*
- * Author : Simon Schulz [ark3116_driver<AT>auctionant.de]
- *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -22,6 +23,8 @@
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <asm/uaccess.h>
static int debug;
@@ -43,10 +46,10 @@ static inline void ARK3116_SND(struct usb_serial *serial, int seq,
{
int result;
result = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev,0),
+ usb_sndctrlpipe(serial->dev, 0),
request, requesttype, value, index,
- NULL,0x00, 1000);
- dbg("%03d > ok",seq);
+ NULL, 0x00, 1000);
+ dbg("%03d > ok", seq);
}
static inline void ARK3116_RCV(struct usb_serial *serial, int seq,
@@ -56,27 +59,25 @@ static inline void ARK3116_RCV(struct usb_serial *serial, int seq,
{
int result;
result = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev,0),
- request, requesttype, value, index,
- buf, 0x0000001, 1000);
+ usb_rcvctrlpipe(serial->dev, 0),
+ request, requesttype, value, index,
+ buf, 0x0000001, 1000);
if (result)
- dbg("%03d < %d bytes [0x%02X]",seq, result, buf[0]);
+ dbg("%03d < %d bytes [0x%02X]", seq, result, buf[0]);
else
dbg("%03d < 0 bytes", seq);
}
-
static inline void ARK3116_RCV_QUIET(struct usb_serial *serial,
__u8 request, __u8 requesttype,
__u16 value, __u16 index, char *buf)
{
usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev,0),
+ usb_rcvctrlpipe(serial->dev, 0),
request, requesttype, value, index,
buf, 0x0000001, 1000);
}
-
static int ark3116_attach(struct usb_serial *serial)
{
char *buf;
@@ -84,10 +85,10 @@ static int ark3116_attach(struct usb_serial *serial)
int i;
for (i = 0; i < serial->num_ports; ++i) {
- priv = kmalloc (sizeof (struct ark3116_private), GFP_KERNEL);
+ priv = kmalloc(sizeof (struct ark3116_private), GFP_KERNEL);
if (!priv)
goto cleanup;
- memset (priv, 0x00, sizeof (struct ark3116_private));
+ memset(priv, 0x00, sizeof (struct ark3116_private));
spin_lock_init(&priv->lock);
usb_set_serial_port_data(serial->port[i], priv);
@@ -95,63 +96,62 @@ static int ark3116_attach(struct usb_serial *serial)
buf = kmalloc(1, GFP_KERNEL);
if (!buf) {
- dbg("error kmalloc -> out of mem ?");
+ dbg("error kmalloc -> out of mem?");
goto cleanup;
}
/* 3 */
- ARK3116_SND(serial, 3,0xFE,0x40,0x0008,0x0002);
- ARK3116_SND(serial, 4,0xFE,0x40,0x0008,0x0001);
- ARK3116_SND(serial, 5,0xFE,0x40,0x0000,0x0008);
- ARK3116_SND(serial, 6,0xFE,0x40,0x0000,0x000B);
+ ARK3116_SND(serial, 3, 0xFE, 0x40, 0x0008, 0x0002);
+ ARK3116_SND(serial, 4, 0xFE, 0x40, 0x0008, 0x0001);
+ ARK3116_SND(serial, 5, 0xFE, 0x40, 0x0000, 0x0008);
+ ARK3116_SND(serial, 6, 0xFE, 0x40, 0x0000, 0x000B);
/* <-- seq7 */
- ARK3116_RCV(serial, 7,0xFE,0xC0,0x0000,0x0003, 0x00, buf);
- ARK3116_SND(serial, 8,0xFE,0x40,0x0080,0x0003);
- ARK3116_SND(serial, 9,0xFE,0x40,0x001A,0x0000);
- ARK3116_SND(serial,10,0xFE,0x40,0x0000,0x0001);
- ARK3116_SND(serial,11,0xFE,0x40,0x0000,0x0003);
+ ARK3116_RCV(serial, 7, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+ ARK3116_SND(serial, 8, 0xFE, 0x40, 0x0080, 0x0003);
+ ARK3116_SND(serial, 9, 0xFE, 0x40, 0x001A, 0x0000);
+ ARK3116_SND(serial, 10, 0xFE, 0x40, 0x0000, 0x0001);
+ ARK3116_SND(serial, 11, 0xFE, 0x40, 0x0000, 0x0003);
/* <-- seq12 */
- ARK3116_RCV(serial,12,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
- ARK3116_SND(serial,13,0xFE,0x40,0x0000,0x0004);
+ ARK3116_RCV(serial, 12, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
+ ARK3116_SND(serial, 13, 0xFE, 0x40, 0x0000, 0x0004);
/* 14 */
- ARK3116_RCV(serial,14,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
- ARK3116_SND(serial,15,0xFE,0x40,0x0000,0x0004);
+ ARK3116_RCV(serial, 14, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
+ ARK3116_SND(serial, 15, 0xFE, 0x40, 0x0000, 0x0004);
/* 16 */
- ARK3116_RCV(serial,16,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
+ ARK3116_RCV(serial, 16, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
/* --> seq17 */
- ARK3116_SND(serial,17,0xFE,0x40,0x0001,0x0004);
+ ARK3116_SND(serial, 17, 0xFE, 0x40, 0x0001, 0x0004);
/* <-- seq18 */
- ARK3116_RCV(serial,18,0xFE,0xC0,0x0000,0x0004, 0x01, buf);
+ ARK3116_RCV(serial, 18, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
/* --> seq19 */
- ARK3116_SND(serial,19,0xFE,0x40,0x0003,0x0004);
-
+ ARK3116_SND(serial, 19, 0xFE, 0x40, 0x0003, 0x0004);
/* <-- seq20 */
- /* seems like serial port status info (RTS, CTS,...) */
- /* returns modem control line status ?! */
- ARK3116_RCV(serial,20,0xFE,0xC0,0x0000,0x0006, 0xFF, buf);
-
- /* set 9600 baud & do some init ?! */
- ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003);
- ARK3116_SND(serial,148,0xFE,0x40,0x0038,0x0000);
- ARK3116_SND(serial,149,0xFE,0x40,0x0001,0x0001);
- ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003);
- ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf);
- ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003);
- ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf);
- ARK3116_SND(serial,154,0xFE,0x40,0x0003,0x0003);
+ /* seems like serial port status info (RTS, CTS, ...) */
+ /* returns modem control line status?! */
+ ARK3116_RCV(serial, 20, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
+
+ /* set 9600 baud & do some init?! */
+ ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
+ ARK3116_SND(serial, 148, 0xFE, 0x40, 0x0038, 0x0000);
+ ARK3116_SND(serial, 149, 0xFE, 0x40, 0x0001, 0x0001);
+ ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
+ ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+ ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
+ ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+ ARK3116_SND(serial, 154, 0xFE, 0x40, 0x0003, 0x0003);
kfree(buf);
- return(0);
+ return 0;
cleanup:
- for (--i; i>=0; --i)
+ for (--i; i >= 0; --i)
usb_set_serial_port_data(serial->port[i], NULL);
return -ENOMEM;
}
@@ -180,7 +180,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
spin_lock_irqsave(&priv->lock, flags);
if (!priv->termios_initialized) {
*(port->tty->termios) = tty_std_termios;
- port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ port->tty->termios->c_cflag = B9600 | CS8
+ | CREAD | HUPCL | CLOCAL;
priv->termios_initialized = 1;
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -204,8 +205,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
}
/* set data bit count (8/7/6/5) */
- if (cflag & CSIZE){
- switch (cflag & CSIZE){
+ if (cflag & CSIZE) {
+ switch (cflag & CSIZE) {
case CS5:
config |= 0x00;
dbg("setting CS5");
@@ -219,7 +220,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
dbg("setting CS7");
break;
default:
- err ("CSIZE was set but not CS5-CS8, using CS8!");
+ err("CSIZE was set but not CS5-CS8, using CS8!");
+ /* fall through */
case CS8:
config |= 0x03;
dbg("setting CS8");
@@ -227,8 +229,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
}
}
- /* set parity (NONE,EVEN,ODD) */
- if (cflag & PARENB){
+ /* set parity (NONE/EVEN/ODD) */
+ if (cflag & PARENB) {
if (cflag & PARODD) {
config |= 0x08;
dbg("setting parity to ODD");
@@ -240,20 +242,19 @@ static void ark3116_set_termios(struct usb_serial_port *port,
dbg("setting parity to NONE");
}
- /* SET STOPBIT (1/2) */
+ /* set stop bit (1/2) */
if (cflag & CSTOPB) {
config |= 0x04;
- dbg ("setting 2 stop bits");
+ dbg("setting 2 stop bits");
} else {
- dbg ("setting 1 stop bit");
+ dbg("setting 1 stop bit");
}
-
- /* set baudrate: */
+ /* set baudrate */
baud = 0;
- switch (cflag & CBAUD){
+ switch (cflag & CBAUD) {
case B0:
- err("can't set 0baud, using 9600 instead");
+ err("can't set 0 baud, using 9600 instead");
break;
case B75: baud = 75; break;
case B150: baud = 150; break;
@@ -285,38 +286,40 @@ static void ark3116_set_termios(struct usb_serial_port *port,
*/
if (baud == 460800)
/* strange, for 460800 the formula is wrong
- * (dont use round(), then 9600baud is wrong) */
+ * if using round() then 9600baud is wrong) */
ark3116_baud = 7;
else
ark3116_baud = 3000000 / baud;
/* ? */
- ARK3116_RCV(serial,0,0xFE,0xC0,0x0000,0x0003, 0x03, buf);
+ ARK3116_RCV(serial, 0, 0xFE, 0xC0, 0x0000, 0x0003, 0x03, buf);
+
/* offset = buf[0]; */
/* offset = 0x03; */
- /* dbg("using 0x%04X as target for 0x0003:",0x0080+offset); */
-
+ /* dbg("using 0x%04X as target for 0x0003:", 0x0080 + offset); */
/* set baudrate */
- dbg("setting baudrate to %d (->reg=%d)",baud,ark3116_baud);
- ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003);
- ARK3116_SND(serial,148,0xFE,0x40,(ark3116_baud & 0x00FF) ,0x0000);
- ARK3116_SND(serial,149,0xFE,0x40,(ark3116_baud & 0xFF00)>>8,0x0001);
- ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003);
+ dbg("setting baudrate to %d (->reg=%d)", baud, ark3116_baud);
+ ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
+ ARK3116_SND(serial, 148, 0xFE, 0x40,
+ (ark3116_baud & 0x00FF), 0x0000);
+ ARK3116_SND(serial, 149, 0xFE, 0x40,
+ (ark3116_baud & 0xFF00) >> 8, 0x0001);
+ ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
/* ? */
- ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf);
- ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003);
+ ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+ ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
/* set data bit count, stop bit count & parity: */
dbg("updating bit count, stop bit or parity (cfg=0x%02X)", config);
- ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf);
- ARK3116_SND(serial,154,0xFE,0x40,config,0x0003);
+ ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+ ARK3116_SND(serial, 154, 0xFE, 0x40, config, 0x0003);
if (cflag & CRTSCTS)
- dbg("CRTSCTS not supported by chipset ?!");
+ dbg("CRTSCTS not supported by chipset?!");
- /* TEST ARK3116_SND(154,0xFE,0x40,0xFFFF, 0x0006); */
+ /* TEST ARK3116_SND(154, 0xFE, 0x40, 0xFFFF, 0x0006); */
kfree(buf);
return;
@@ -329,11 +332,11 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
char *buf;
int result = 0;
- dbg("%s - port %d", __FUNCTION__, port->number);
+ dbg("%s - port %d", __FUNCTION__, port->number);
buf = kmalloc(1, GFP_KERNEL);
if (!buf) {
- dbg("error kmalloc -> out of mem ?");
+ dbg("error kmalloc -> out of mem?");
return -ENOMEM;
}
@@ -342,44 +345,68 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
return result;
/* open */
- ARK3116_RCV(serial,111,0xFE,0xC0,0x0000,0x0003, 0x02, buf);
+ ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf);
- ARK3116_SND(serial,112,0xFE,0x40,0x0082,0x0003);
- ARK3116_SND(serial,113,0xFE,0x40,0x001A,0x0000);
- ARK3116_SND(serial,114,0xFE,0x40,0x0000,0x0001);
- ARK3116_SND(serial,115,0xFE,0x40,0x0002,0x0003);
+ ARK3116_SND(serial, 112, 0xFE, 0x40, 0x0082, 0x0003);
+ ARK3116_SND(serial, 113, 0xFE, 0x40, 0x001A, 0x0000);
+ ARK3116_SND(serial, 114, 0xFE, 0x40, 0x0000, 0x0001);
+ ARK3116_SND(serial, 115, 0xFE, 0x40, 0x0002, 0x0003);
- ARK3116_RCV(serial,116,0xFE,0xC0,0x0000,0x0004, 0x03, buf);
- ARK3116_SND(serial,117,0xFE,0x40,0x0002,0x0004);
+ ARK3116_RCV(serial, 116, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+ ARK3116_SND(serial, 117, 0xFE, 0x40, 0x0002, 0x0004);
- ARK3116_RCV(serial,118,0xFE,0xC0,0x0000,0x0004, 0x02, buf);
- ARK3116_SND(serial,119,0xFE,0x40,0x0000,0x0004);
+ ARK3116_RCV(serial, 118, 0xFE, 0xC0, 0x0000, 0x0004, 0x02, buf);
+ ARK3116_SND(serial, 119, 0xFE, 0x40, 0x0000, 0x0004);
- ARK3116_RCV(serial,120,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
+ ARK3116_RCV(serial, 120, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
- ARK3116_SND(serial,121,0xFE,0x40,0x0001,0x0004);
+ ARK3116_SND(serial, 121, 0xFE, 0x40, 0x0001, 0x0004);
- ARK3116_RCV(serial,122,0xFE,0xC0,0x0000,0x0004, 0x01, buf);
+ ARK3116_RCV(serial, 122, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
- ARK3116_SND(serial,123,0xFE,0x40,0x0003,0x0004);
+ ARK3116_SND(serial, 123, 0xFE, 0x40, 0x0003, 0x0004);
- /* returns different values (control lines ?!) */
- ARK3116_RCV(serial,124,0xFE,0xC0,0x0000,0x0006, 0xFF, buf);
+ /* returns different values (control lines?!) */
+ ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
- /* initialise termios: */
+ /* initialise termios */
if (port->tty)
ark3116_set_termios(port, &tmp_termios);
kfree(buf);
return result;
-
}
static int ark3116_ioctl(struct usb_serial_port *port, struct file *file,
unsigned int cmd, unsigned long arg)
{
- dbg("ioctl not supported yet...");
+ struct serial_struct serstruct;
+ void __user *user_arg = (void __user *)arg;
+
+ switch (cmd) {
+ case TIOCGSERIAL:
+ /* XXX: Some of these values are probably wrong. */
+ memset(&serstruct, 0, sizeof (serstruct));
+ serstruct.type = PORT_16654;
+ serstruct.line = port->serial->minor;
+ serstruct.port = port->number;
+ serstruct.custom_divisor = 0;
+ serstruct.baud_base = 460800;
+
+ if (copy_to_user(user_arg, &serstruct, sizeof (serstruct)))
+ return -EFAULT;
+
+ return 0;
+ case TIOCSSERIAL:
+ if (copy_from_user(&serstruct, user_arg, sizeof (serstruct)))
+ return -EFAULT;
+ return 0;
+ default:
+ dbg("%s cmd 0x%04x not supported", __FUNCTION__, cmd);
+ break;
+ }
+
return -ENOIOCTLCMD;
}
@@ -389,7 +416,7 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file)
char *buf;
char temp;
- /* seems like serial port status info (RTS, CTS,...) is stored
+ /* seems like serial port status info (RTS, CTS, ...) is stored
* in reg(?) 0x0006
* pcb connection point 11 = GND -> sets bit4 of response
* pcb connection point 7 = GND -> sets bit6 of response
@@ -401,16 +428,16 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file)
return -ENOMEM;
}
- /* read register: */
- ARK3116_RCV_QUIET(serial,0xFE,0xC0,0x0000,0x0006,buf);
+ /* read register */
+ ARK3116_RCV_QUIET(serial, 0xFE, 0xC0, 0x0000, 0x0006, buf);
temp = buf[0];
kfree(buf);
- /* i do not really know if bit4=CTS and bit6=DSR... was just a
- * quick guess !!
+ /* i do not really know if bit4=CTS and bit6=DSR... just a
+ * quick guess!
*/
- return (temp & (1<<4) ? TIOCM_CTS : 0) |
- (temp & (1<<6) ? TIOCM_DSR : 0);
+ return (temp & (1<<4) ? TIOCM_CTS : 0)
+ | (temp & (1<<6) ? TIOCM_DSR : 0);
}
static struct usb_driver ark3116_driver = {
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 6286aba86fa..d954ec34b01 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -214,14 +214,14 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b
return (0);
}
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
spin_lock_irqsave(&priv->lock, flags);
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index ee70fddcab6..e1173c1aee3 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -129,6 +129,9 @@ struct cypress_private {
int cmd_ctrl; /* always set this to 1 before issuing a command */
struct cypress_buf *buf; /* write buffer */
int write_urb_in_use; /* write urb in use indicator */
+ int write_urb_interval; /* interval to use for write urb */
+ int read_urb_interval; /* interval to use for read urb */
+ int comm_is_ok; /* true if communication is (still) ok */
int termios_initialized;
__u8 line_control; /* holds dtr / rts value */
__u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */
@@ -168,6 +171,7 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file,
static int cypress_chars_in_buffer (struct usb_serial_port *port);
static void cypress_throttle (struct usb_serial_port *port);
static void cypress_unthrottle (struct usb_serial_port *port);
+static void cypress_set_dead (struct usb_serial_port *port);
static void cypress_read_int_callback (struct urb *urb, struct pt_regs *regs);
static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs);
/* baud helper functions */
@@ -288,6 +292,9 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
priv = usb_get_serial_port_data(port);
+ if (!priv->comm_is_ok)
+ return -ENODEV;
+
switch(cypress_request_type) {
case CYPRESS_SET_CONFIG:
@@ -365,13 +372,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
if (tries++ >= 3)
break;
- if (retval == EPIPE)
- usb_clear_halt(port->serial->dev, 0x00);
- } while (retval != 8 && retval != ENODEV);
+ } while (retval != 8 && retval != -ENODEV);
- if (retval != 8)
+ if (retval != 8) {
err("%s - failed sending serial line settings - %d", __FUNCTION__, retval);
- else {
+ cypress_set_dead(port);
+ } else {
spin_lock_irqsave(&priv->lock, flags);
priv->baud_rate = new_baudrate;
priv->cbr_mask = baud_mask;
@@ -392,12 +398,11 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
if (tries++ >= 3)
break;
- if (retval == EPIPE)
- usb_clear_halt(port->serial->dev, 0x00);
- } while (retval != 5 && retval != ENODEV);
+ } while (retval != 5 && retval != -ENODEV);
if (retval != 5) {
err("%s - failed to retrieve serial line settings - %d", __FUNCTION__, retval);
+ cypress_set_dead(port);
return retval;
} else {
spin_lock_irqsave(&priv->lock, flags);
@@ -419,6 +424,24 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
} /* cypress_serial_control */
+static void cypress_set_dead(struct usb_serial_port *port)
+{
+ struct cypress_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!priv->comm_is_ok) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
+ }
+ priv->comm_is_ok = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ err("cypress_m8 suspending failing port %d - interval might be too short",
+ port->number);
+}
+
+
/* given a baud mask, it will return integer baud on success */
static int mask_to_rate (unsigned mask)
{
@@ -472,13 +495,15 @@ static unsigned rate_to_mask (int rate)
static int generic_startup (struct usb_serial *serial)
{
struct cypress_private *priv;
+ struct usb_serial_port *port = serial->port[0];
- dbg("%s - port %d", __FUNCTION__, serial->port[0]->number);
+ dbg("%s - port %d", __FUNCTION__, port->number);
priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->comm_is_ok = !0;
spin_lock_init(&priv->lock);
priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE);
if (priv->buf == NULL) {
@@ -489,13 +514,24 @@ static int generic_startup (struct usb_serial *serial)
usb_reset_configuration (serial->dev);
- interval = 1;
priv->cmd_ctrl = 0;
priv->line_control = 0;
priv->termios_initialized = 0;
priv->rx_flags = 0;
priv->cbr_mask = B300;
- usb_set_serial_port_data(serial->port[0], priv);
+ if (interval > 0) {
+ priv->write_urb_interval = interval;
+ priv->read_urb_interval = interval;
+ dbg("%s - port %d read & write intervals forced to %d",
+ __FUNCTION__,port->number,interval);
+ } else {
+ priv->write_urb_interval = port->interrupt_out_urb->interval;
+ priv->read_urb_interval = port->interrupt_in_urb->interval;
+ dbg("%s - port %d intervals: read=%d write=%d",
+ __FUNCTION__,port->number,
+ priv->read_urb_interval,priv->write_urb_interval);
+ }
+ usb_set_serial_port_data(port, priv);
return 0;
}
@@ -585,6 +621,9 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp)
dbg("%s - port %d", __FUNCTION__, port->number);
+ if (!priv->comm_is_ok)
+ return -EIO;
+
/* clear halts before open */
usb_clear_halt(serial->dev, 0x81);
usb_clear_halt(serial->dev, 0x02);
@@ -624,11 +663,12 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp)
usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length,
- cypress_read_int_callback, port, interval);
+ cypress_read_int_callback, port, priv->read_urb_interval);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result){
dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
+ cypress_set_dead(port);
}
return result;
@@ -733,6 +773,9 @@ static void cypress_send(struct usb_serial_port *port)
struct cypress_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
+ if (!priv->comm_is_ok)
+ return;
+
dbg("%s - port %d", __FUNCTION__, port->number);
dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size);
@@ -806,14 +849,16 @@ send:
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size,
port->interrupt_out_urb->transfer_buffer);
- port->interrupt_out_urb->transfer_buffer_length = actual_size;
- port->interrupt_out_urb->dev = port->serial->dev;
- port->interrupt_out_urb->interval = interval;
+ usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
+ usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
+ port->interrupt_out_buffer, port->interrupt_out_size,
+ cypress_write_int_callback, port, priv->write_urb_interval);
result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC);
if (result) {
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__,
result);
priv->write_urb_in_use = 0;
+ cypress_set_dead(port);
}
spin_lock_irqsave(&priv->lock, flags);
@@ -1214,13 +1259,18 @@ static void cypress_unthrottle (struct usb_serial_port *port)
priv->rx_flags = 0;
spin_unlock_irqrestore(&priv->lock, flags);
+ if (!priv->comm_is_ok)
+ return;
+
if (actually_throttled) {
port->interrupt_in_urb->dev = port->serial->dev;
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
- if (result)
+ if (result) {
dev_err(&port->dev, "%s - failed submitting read urb, "
"error %d\n", __FUNCTION__, result);
+ cypress_set_dead(port);
+ }
}
}
@@ -1240,9 +1290,22 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
dbg("%s - port %d", __FUNCTION__, port->number);
- if (urb->status) {
- dbg("%s - nonzero read status received: %d", __FUNCTION__,
- urb->status);
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* precursor to disconnect so just go away */
+ return;
+ case -EPIPE:
+ usb_clear_halt(port->serial->dev,0x81);
+ break;
+ default:
+ /* something ugly is going on... */
+ dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n",
+ __FUNCTION__,urb->status);
+ cypress_set_dead(port);
return;
}
@@ -1343,18 +1406,20 @@ continue_read:
/* Continue trying to always read... unless the port has closed. */
- if (port->open_count > 0) {
+ if (port->open_count > 0 && priv->comm_is_ok) {
usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
usb_rcvintpipe(port->serial->dev,
port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer,
port->interrupt_in_urb->transfer_buffer_length,
- cypress_read_int_callback, port, interval);
+ cypress_read_int_callback, port, priv->read_urb_interval);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
- if (result)
+ if (result) {
dev_err(&urb->dev->dev, "%s - failed resubmitting "
"read urb, error %d\n", __FUNCTION__,
result);
+ cypress_set_dead(port);
+ }
}
return;
@@ -1380,20 +1445,26 @@ static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs)
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
priv->write_urb_in_use = 0;
return;
- case -EPIPE: /* no break needed */
+ case -EPIPE: /* no break needed; clear halt and resubmit */
+ if (!priv->comm_is_ok)
+ break;
usb_clear_halt(port->serial->dev, 0x02);
- default:
/* error in the urb, so we have to resubmit it */
- dbg("%s - Overflow in write", __FUNCTION__);
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
port->interrupt_out_urb->transfer_buffer_length = 1;
port->interrupt_out_urb->dev = port->serial->dev;
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
- if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n",
- __FUNCTION__, result);
- else
+ if (!result)
return;
+ dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n",
+ __FUNCTION__, result);
+ cypress_set_dead(port);
+ break;
+ default:
+ dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n",
+ __FUNCTION__,urb->status);
+ cypress_set_dead(port);
+ break;
}
priv->write_urb_in_use = 0;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index c6115aa1b44..e774a27c6c9 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -344,6 +344,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) },
+ { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) },
@@ -507,6 +508,9 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) },
{ USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
@@ -1101,25 +1105,29 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer);
static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char);
-static void create_sysfs_attrs(struct usb_serial *serial)
-{
+static int create_sysfs_attrs(struct usb_serial *serial)
+{
struct ftdi_private *priv;
struct usb_device *udev;
+ int retval = 0;
dbg("%s",__FUNCTION__);
-
+
priv = usb_get_serial_port_data(serial->port[0]);
udev = serial->dev;
-
+
/* XXX I've no idea if the original SIO supports the event_char
* sysfs parameter, so I'm playing it safe. */
if (priv->chip_type != SIO) {
dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
- device_create_file(&udev->dev, &dev_attr_event_char);
- if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
- device_create_file(&udev->dev, &dev_attr_latency_timer);
+ retval = device_create_file(&udev->dev, &dev_attr_event_char);
+ if ((!retval) &&
+ (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
+ retval = device_create_file(&udev->dev,
+ &dev_attr_latency_timer);
}
}
+ return retval;
}
static void remove_sysfs_attrs(struct usb_serial *serial)
@@ -1162,7 +1170,8 @@ static int ftdi_sio_attach (struct usb_serial *serial)
struct usb_serial_port *port = serial->port[0];
struct ftdi_private *priv;
struct ftdi_sio_quirk *quirk;
-
+ int retval;
+
dbg("%s",__FUNCTION__);
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
@@ -1203,15 +1212,18 @@ static int ftdi_sio_attach (struct usb_serial *serial)
usb_set_serial_port_data(serial->port[0], priv);
ftdi_determine_type (serial->port[0]);
- create_sysfs_attrs(serial);
+ retval = create_sysfs_attrs(serial);
+ if (retval)
+ dev_err(&serial->dev->dev, "Error creating sysfs files, "
+ "continuing\n");
/* Check for device requiring special set up. */
quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial);
if (quirk && quirk->setup) {
quirk->setup(serial);
}
-
- return (0);
+
+ return 0;
} /* ftdi_sio_attach */
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 77299996f7e..f0edb87d2dd 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -111,6 +111,7 @@
#define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */
#define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */
#define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */
+#define SEALEVEL_2106_PID 0x9020 /* SeaLINK+422 (2106) */
#define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */
#define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */
#define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */
@@ -472,6 +473,15 @@
*/
#define FTDI_GAMMA_SCOUT_PID 0xD678 /* Gamma Scout online */
+/*
+ * Tactrix OpenPort (ECU) devices.
+ * OpenPort 1.3M submitted by Donour Sizemore.
+ * OpenPort 1.3S and 1.3U submitted by Ian Abbott.
+ */
+#define FTDI_TACTRIX_OPENPORT_13M_PID 0xCC48 /* OpenPort 1.3 Mitsubishi */
+#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */
+#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 727852634be..4b1196a8b09 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1,7 +1,7 @@
/*
* Garmin GPS driver
*
- * Copyright (C) 2004 Hermann Kneissel herkne@users.sourceforge.net
+ * Copyright (C) 2006 Hermann Kneissel herkne@users.sourceforge.net
*
* The latest version of the driver can be found at
* http://sourceforge.net/projects/garmin-gps/
@@ -37,6 +37,8 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
+#include <linux/version.h>
+
/* the mode to be set when the port ist opened */
static int initial_mode = 1;
@@ -50,7 +52,7 @@ static int debug = 0;
*/
#define VERSION_MAJOR 0
-#define VERSION_MINOR 23
+#define VERSION_MINOR 28
#define _STR(s) #s
#define _DRIVER_VERSION(a,b) "v" _STR(a) "." _STR(b)
@@ -164,7 +166,8 @@ struct garmin_data {
#define FLAGS_SESSION_REPLY1_SEEN 0x0080
#define FLAGS_SESSION_REPLY2_SEEN 0x0040
#define FLAGS_BULK_IN_ACTIVE 0x0020
-#define FLAGS_THROTTLED 0x0010
+#define FLAGS_BULK_IN_RESTART 0x0010
+#define FLAGS_THROTTLED 0x0008
#define CLEAR_HALT_REQUIRED 0x0001
#define FLAGS_QUEUING 0x0100
@@ -224,7 +227,7 @@ static struct usb_driver garmin_driver = {
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
- .no_dynamic_id = 1,
+ .no_dynamic_id = 1,
};
@@ -270,7 +273,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf)
static void send_to_tty(struct usb_serial_port *port,
- char *data, unsigned int actual_length)
+ char *data, unsigned int actual_length)
{
struct tty_struct *tty = port->tty;
@@ -294,15 +297,15 @@ static void send_to_tty(struct usb_serial_port *port,
* queue a received (usb-)packet for later processing
*/
static int pkt_add(struct garmin_data * garmin_data_p,
- unsigned char *data, unsigned int data_length)
+ unsigned char *data, unsigned int data_length)
{
+ int state = 0;
int result = 0;
unsigned long flags;
struct garmin_packet *pkt;
/* process only packets containg data ... */
if (data_length) {
- garmin_data_p->flags |= FLAGS_QUEUING;
pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
GFP_ATOMIC);
if (pkt == NULL) {
@@ -313,14 +316,16 @@ static int pkt_add(struct garmin_data * garmin_data_p,
memcpy(pkt->data, data, data_length);
spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags |= FLAGS_QUEUING;
result = list_empty(&garmin_data_p->pktlist);
pkt->seq = garmin_data_p->seq_counter++;
list_add_tail(&pkt->list, &garmin_data_p->pktlist);
+ state = garmin_data_p->state;
spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* in serial mode, if someone is waiting for data from
the device, iconvert and send the next packet to tty. */
- if (result && (garmin_data_p->state == STATE_GSP_WAIT_DATA)) {
+ if (result && (state == STATE_GSP_WAIT_DATA)) {
gsp_next_packet(garmin_data_p);
}
}
@@ -370,9 +375,9 @@ static void pkt_clear(struct garmin_data * garmin_data_p)
static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id)
{
__u8 pkt[10];
- __u8 cksum = 0;
- __u8 *ptr = pkt;
- unsigned l = 0;
+ __u8 cksum = 0;
+ __u8 *ptr = pkt;
+ unsigned l = 0;
dbg("%s - pkt-id: 0x%X.", __FUNCTION__, 0xFF & pkt_id);
@@ -416,7 +421,7 @@ static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id)
static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
{
const __u8* recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET;
- __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
+ __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
int cksum = 0;
int n = 0;
@@ -447,11 +452,11 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
n++;
}
- if ((0xff & (cksum + *recpkt)) != 0) {
- dbg("%s - invalid checksum, expected %02x, got %02x",
- __FUNCTION__, 0xff & -cksum, 0xff & *recpkt);
- return -EINVPKT;
- }
+ if ((0xff & (cksum + *recpkt)) != 0) {
+ dbg("%s - invalid checksum, expected %02x, got %02x",
+ __FUNCTION__, 0xff & -cksum, 0xff & *recpkt);
+ return -EINVPKT;
+ }
usbdata[0] = __cpu_to_le32(GARMIN_LAYERID_APPL);
usbdata[1] = __cpu_to_le32(pktid);
@@ -491,20 +496,28 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
*/
static int gsp_receive(struct garmin_data * garmin_data_p,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
int offs = 0;
int ack_or_nak_seen = 0;
int i = 0;
- __u8 *dest = garmin_data_p->inbuffer;
- int size = garmin_data_p->insize;
+ __u8 *dest;
+ int size;
// dleSeen: set if last byte read was a DLE
- int dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+ int dleSeen;
// skip: if set, skip incoming data until possible start of
// new packet
- int skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+ int skip;
__u8 data;
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ dest = garmin_data_p->inbuffer;
+ size = garmin_data_p->insize;
+ dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+ skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
dbg("%s - dle=%d skip=%d size=%d count=%d",
__FUNCTION__, dleSeen, skip, size, count);
@@ -572,6 +585,8 @@ static int gsp_receive(struct garmin_data * garmin_data_p,
}
}
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+
garmin_data_p->insize = size;
// copy flags back to structure
@@ -587,6 +602,11 @@ static int gsp_receive(struct garmin_data * garmin_data_p,
if (ack_or_nak_seen) {
garmin_data_p->state = STATE_GSP_WAIT_DATA;
+ }
+
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+ if (ack_or_nak_seen) {
gsp_next_packet(garmin_data_p);
}
@@ -676,7 +696,7 @@ static int gsp_send(struct garmin_data * garmin_data_p,
src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
if (k > (GARMIN_PKTHDR_LENGTH-2)) {
/* can't add stuffing DLEs in place, move data to end
- of buffer ... */
+ of buffer ... */
dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen;
memcpy(dst, src, datalen);
src = dst;
@@ -755,8 +775,9 @@ static void gsp_next_packet(struct garmin_data * garmin_data_p)
* or even incomplete packets
*/
static int nat_receive(struct garmin_data * garmin_data_p,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
__u8 * dest;
int offs = 0;
int result = count;
@@ -803,7 +824,9 @@ static int nat_receive(struct garmin_data * garmin_data_p,
/* if this was an abort-transfer command,
flush all queued data. */
if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_DROP_DATA;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
pkt_clear(garmin_data_p);
}
}
@@ -839,12 +862,15 @@ static void priv_status_resp(struct usb_serial_port *port)
static int process_resetdev_request(struct usb_serial_port *port)
{
+ unsigned long flags;
int status;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED);
garmin_data_p->state = STATE_RESET;
garmin_data_p->serial_num = 0;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
usb_kill_urb (port->interrupt_in_urb);
dbg("%s - usb_reset_device", __FUNCTION__ );
@@ -862,6 +888,7 @@ static int process_resetdev_request(struct usb_serial_port *port)
*/
static int garmin_clear(struct garmin_data * garmin_data_p)
{
+ unsigned long flags;
int status = 0;
struct usb_serial_port *port = garmin_data_p->port;
@@ -875,8 +902,10 @@ static int garmin_clear(struct garmin_data * garmin_data_p)
/* flush all queued data */
pkt_clear(garmin_data_p);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->insize = 0;
garmin_data_p->outsize = 0;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
return status;
}
@@ -888,6 +917,7 @@ static int garmin_clear(struct garmin_data * garmin_data_p)
static int garmin_init_session(struct usb_serial_port *port)
{
+ unsigned long flags;
struct usb_serial *serial = port->serial;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
int status = 0;
@@ -913,7 +943,9 @@ static int garmin_init_session(struct usb_serial_port *port)
if (status >= 0) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->ignorePkts++;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* not needed, but the win32 driver does it too ... */
status = garmin_write_bulk(port,
@@ -921,7 +953,9 @@ static int garmin_init_session(struct usb_serial_port *port)
sizeof(GARMIN_START_SESSION_REQ2));
if (status >= 0) {
status = 0;
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->ignorePkts++;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
}
}
@@ -935,6 +969,7 @@ static int garmin_init_session(struct usb_serial_port *port)
static int garmin_open (struct usb_serial_port *port, struct file *filp)
{
+ unsigned long flags;
int status = 0;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
@@ -948,9 +983,11 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp)
if (port->tty)
port->tty->low_latency = 1;
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->mode = initial_mode;
garmin_data_p->count = 0;
garmin_data_p->flags = 0;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* shutdown any bulk reads that might be going on */
usb_kill_urb (port->write_urb);
@@ -996,6 +1033,7 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp)
static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
+ unsigned long flags;
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
@@ -1007,7 +1045,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
if (urb->status) {
dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
usb_serial_port_softint(port);
@@ -1015,8 +1055,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
static int garmin_write_bulk (struct usb_serial_port *port,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
struct usb_serial *serial = port->serial;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
struct urb *urb;
@@ -1026,7 +1067,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
dbg("%s - port %d, state %d", __FUNCTION__, port->number,
garmin_data_p->state);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~FLAGS_DROP_DATA;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
buffer = kmalloc (count, GFP_ATOMIC);
if (!buffer) {
@@ -1053,7 +1096,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
urb->transfer_flags |= URB_ZERO_PACKET;
if (GARMIN_LAYERID_APPL == getLayerId(buffer)) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
pkt_clear(garmin_data_p);
garmin_data_p->state = STATE_GSP_WAIT_DATA;
@@ -1087,8 +1132,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
static int garmin_write (struct usb_serial_port *port,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
int pktid, pktsiz, len;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
__le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
@@ -1139,7 +1185,9 @@ static int garmin_write (struct usb_serial_port *port,
break;
case PRIV_PKTID_RESET_REQ:
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
break;
case PRIV_PKTID_SET_DEF_MODE:
@@ -1155,6 +1203,8 @@ static int garmin_write (struct usb_serial_port *port,
}
}
+ garmin_data_p->ignorePkts = 0;
+
if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
return gsp_receive(garmin_data_p, buf, count);
} else { /* MODE_NATIVE */
@@ -1177,10 +1227,10 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port)
{
/*
* Report back the number of bytes currently in our input buffer.
- * Will this lock up the driver - the buffer contains an incomplete
- * package which will not be written to the device until it
- * has been completed ?
- */
+ * Will this lock up the driver - the buffer contains an incomplete
+ * package which will not be written to the device until it
+ * has been completed ?
+ */
//struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
//return garmin_data_p->insize;
return 0;
@@ -1190,6 +1240,8 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port)
static void garmin_read_process(struct garmin_data * garmin_data_p,
unsigned char *data, unsigned data_length)
{
+ unsigned long flags;
+
if (garmin_data_p->flags & FLAGS_DROP_DATA) {
/* abort-transfer cmd is actice */
dbg("%s - pkt dropped", __FUNCTION__);
@@ -1200,11 +1252,14 @@ static void garmin_read_process(struct garmin_data * garmin_data_p,
if a reset is required or not when closing
the device */
if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY,
- sizeof(GARMIN_APP_LAYER_REPLY)))
+ sizeof(GARMIN_APP_LAYER_REPLY))) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_APP_RESP_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+ }
/* if throttling is active or postprecessing is required
- put the received data in th input queue, otherwise
+ put the received data in the input queue, otherwise
send it directly to the tty port */
if (garmin_data_p->flags & FLAGS_QUEUING) {
pkt_add(garmin_data_p, data, data_length);
@@ -1221,6 +1276,7 @@ static void garmin_read_process(struct garmin_data * garmin_data_p,
static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
+ unsigned long flags;
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
@@ -1245,19 +1301,30 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
garmin_read_process(garmin_data_p, data, urb->actual_length);
- /* Continue trying to read until nothing more is received */
- if (urb->actual_length > 0) {
- usb_fill_bulk_urb (port->read_urb, serial->dev,
- usb_rcvbulkpipe (serial->dev,
- port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer,
- port->read_urb->transfer_buffer_length,
- garmin_read_bulk_callback, port);
+ if (urb->actual_length == 0 &&
+ 0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (status)
dev_err(&port->dev,
"%s - failed resubmitting read urb, error %d\n",
__FUNCTION__, status);
+ } else if (urb->actual_length > 0) {
+ /* Continue trying to read until nothing more is received */
+ if (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) {
+ status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (status)
+ dev_err(&port->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, status);
+ }
+ } else {
+ dbg("%s - end of bulk data", __FUNCTION__);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags &= ~FLAGS_BULK_IN_ACTIVE;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
return;
}
@@ -1265,6 +1332,7 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
{
+ unsigned long flags;
int status;
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
@@ -1297,25 +1365,41 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
dbg("%s - bulk data available.", __FUNCTION__);
- /* bulk data available */
- usb_fill_bulk_urb (port->read_urb, serial->dev,
- usb_rcvbulkpipe (serial->dev,
- port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer,
- port->read_urb->transfer_buffer_length,
- garmin_read_bulk_callback, port);
- status = usb_submit_urb(port->read_urb, GFP_KERNEL);
- if (status) {
- dev_err(&port->dev,
- "%s - failed submitting read urb, error %d\n",
- __FUNCTION__, status);
+ if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+
+ /* bulk data available */
+ usb_fill_bulk_urb (port->read_urb, serial->dev,
+ usb_rcvbulkpipe (serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ garmin_read_bulk_callback, port);
+ status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (status) {
+ dev_err(&port->dev,
+ "%s - failed submitting read urb, error %d\n",
+ __FUNCTION__, status);
+ } else {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE;
+ /* do not send this packet to the user */
+ garmin_data_p->ignorePkts = 1;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+ }
+ } else {
+ /* bulk-in transfer still active */
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags |= FLAGS_BULK_IN_RESTART;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
} else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY))
&& 0 == memcmp(data, GARMIN_START_SESSION_REPLY,
sizeof(GARMIN_START_SESSION_REPLY))) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* save the serial number */
garmin_data_p->serial_num
@@ -1330,7 +1414,9 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
ignore it. */
dbg("%s - pkt ignored (%d)",
__FUNCTION__, garmin_data_p->ignorePkts);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->ignorePkts--;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
} else {
garmin_read_process(garmin_data_p, data, urb->actual_length);
}
@@ -1351,18 +1437,20 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
*/
static int garmin_flush_queue(struct garmin_data * garmin_data_p)
{
+ unsigned long flags;
struct garmin_packet *pkt;
if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
pkt = pkt_pop(garmin_data_p);
if (pkt != NULL) {
-
send_to_tty(garmin_data_p->port, pkt->data, pkt->size);
kfree(pkt);
mod_timer(&garmin_data_p->timer, (1)+jiffies);
} else {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~FLAGS_QUEUING;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
}
return 0;
@@ -1371,26 +1459,41 @@ static int garmin_flush_queue(struct garmin_data * garmin_data_p)
static void garmin_throttle (struct usb_serial_port *port)
{
+ unsigned long flags;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
dbg("%s - port %d", __FUNCTION__, port->number);
/* set flag, data received will be put into a queue
for later processing */
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
static void garmin_unthrottle (struct usb_serial_port *port)
{
+ unsigned long flags;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+ int status;
dbg("%s - port %d", __FUNCTION__, port->number);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~FLAGS_THROTTLED;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* in native mode send queued data to tty, in
serial mode nothing needs to be done here */
if (garmin_data_p->mode == MODE_NATIVE)
garmin_flush_queue(garmin_data_p);
+
+ if (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+ status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (status)
+ dev_err(&port->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, status);
+ }
}
@@ -1420,11 +1523,12 @@ static int garmin_attach (struct usb_serial *serial)
dbg("%s", __FUNCTION__);
- garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
+ garmin_data_p = kmalloc (sizeof(struct garmin_data), GFP_KERNEL);
if (garmin_data_p == NULL) {
dev_err(&port->dev, "%s - Out of memory\n", __FUNCTION__);
return -ENOMEM;
}
+ memset (garmin_data_p, 0, sizeof(struct garmin_data));
init_timer(&garmin_data_p->timer);
spin_lock_init(&garmin_data_p->lock);
INIT_LIST_HEAD(&garmin_data_p->pktlist);
@@ -1459,10 +1563,10 @@ static void garmin_shutdown (struct usb_serial *serial)
/* All of the device info needed */
static struct usb_serial_driver garmin_device = {
.driver = {
- .owner = THIS_MODULE,
- .name = "garmin_gps",
+ .owner = THIS_MODULE,
+ .name = "garmin_gps",
},
- .description = "Garmin GPS usb/tty",
+ .description = "Garmin GPS usb/tty",
.id_table = id_table,
.num_interrupt_in = 1,
.num_bulk_in = 1,
@@ -1483,6 +1587,7 @@ static struct usb_serial_driver garmin_device = {
};
+
static int __init garmin_init (void)
{
int retval;
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 17271355639..21cbaa0fb96 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -175,14 +175,14 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *
/* only do something if we have a bulk out endpoint */
if (serial->num_bulk_out) {
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index 9840bade79f..cbc725a6c58 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -479,6 +479,7 @@ static struct usb_device_id ipaq_id_table [] = {
{ USB_DEVICE(0x0BB4, 0x0A9D) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9E) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9F) }, /* SmartPhone USB Sync */
+ { USB_DEVICE(0x0BB4, 0x0BCE) }, /* "High Tech Computer Corp" */
{ USB_DEVICE(0x0BF8, 0x1001) }, /* Fujitsu Siemens Computers USB Sync */
{ USB_DEVICE(0x0C44, 0x03A2) }, /* Motorola iDEN Smartphone */
{ USB_DEVICE(0x0C8E, 0x6000) }, /* Cesscom Luxian Series */
@@ -652,11 +653,6 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE;
msleep(1000*initial_wait);
- /* Start reading from the device */
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
- ipaq_read_bulk_callback, port);
/*
* Send out control message observed in win98 sniffs. Not sure what
@@ -670,18 +666,31 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
0x1, 0, NULL, 0, 100);
- if (result == 0) {
- result = usb_submit_urb(port->read_urb, GFP_KERNEL);
- if (result) {
- err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
- goto error;
- }
- return 0;
- }
+ if (!result)
+ break;
+
msleep(1000);
}
- err("%s - failed doing control urb, error %d", __FUNCTION__, result);
- goto error;
+
+ if (!retries && result) {
+ err("%s - failed doing control urb, error %d", __FUNCTION__,
+ result);
+ goto error;
+ }
+
+ /* Start reading from the device */
+ usb_fill_bulk_urb(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+ ipaq_read_bulk_callback, port);
+
+ result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ if (result) {
+ err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+ goto error;
+ }
+
+ return 0;
enomem:
result = -ENOMEM;
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 87306cb6f9f..812bc213a96 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -394,14 +394,14 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int
return 0;
}
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
count = min(count, port->bulk_out_size);
memcpy(port->bulk_out_buffer, buf, count);
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 1738b0b6a37..1b348df388e 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -342,14 +342,14 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int
if (count == 0)
return 0;
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
transfer_buffer = port->write_urb->transfer_buffer;
transfer_size = min(count, port->bulk_out_size - 1);
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 49b8dc039d1..59e777f1e8f 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -518,13 +518,13 @@ static int keyspan_pda_write(struct usb_serial_port *port,
the TX urb is in-flight (wait until it completes)
the device is full (wait until it says there is room)
*/
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy || priv->tx_throttled) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
/* At this point the URB is in our control, nobody else can submit it
again (the only sudden transition was the one from EINPROGRESS to
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
new file mode 100644
index 00000000000..95bf57166c5
--- /dev/null
+++ b/drivers/usb/serial/mos7840.c
@@ -0,0 +1,2962 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Clean ups from Moschip version and a few ioctl implementations by:
+ * Paul B Schroeder <pschroeder "at" uplogix "dot" com>
+ *
+ * Originally based on drivers/usb/serial/io_edgeport.c which is:
+ * Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <asm/uaccess.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "1.3.1"
+#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver"
+
+/*
+ * 16C50 UART register defines
+ */
+
+#define LCR_BITS_5 0x00 /* 5 bits/char */
+#define LCR_BITS_6 0x01 /* 6 bits/char */
+#define LCR_BITS_7 0x02 /* 7 bits/char */
+#define LCR_BITS_8 0x03 /* 8 bits/char */
+#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */
+
+#define LCR_STOP_1 0x00 /* 1 stop bit */
+#define LCR_STOP_1_5 0x04 /* 1.5 stop bits (if 5 bits/char) */
+#define LCR_STOP_2 0x04 /* 2 stop bits (if 6-8 bits/char) */
+#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */
+
+#define LCR_PAR_NONE 0x00 /* No parity */
+#define LCR_PAR_ODD 0x08 /* Odd parity */
+#define LCR_PAR_EVEN 0x18 /* Even parity */
+#define LCR_PAR_MARK 0x28 /* Force parity bit to 1 */
+#define LCR_PAR_SPACE 0x38 /* Force parity bit to 0 */
+#define LCR_PAR_MASK 0x38 /* Mask for parity field */
+
+#define LCR_SET_BREAK 0x40 /* Set Break condition */
+#define LCR_DL_ENABLE 0x80 /* Enable access to divisor latch */
+
+#define MCR_DTR 0x01 /* Assert DTR */
+#define MCR_RTS 0x02 /* Assert RTS */
+#define MCR_OUT1 0x04 /* Loopback only: Sets state of RI */
+#define MCR_MASTER_IE 0x08 /* Enable interrupt outputs */
+#define MCR_LOOPBACK 0x10 /* Set internal (digital) loopback mode */
+#define MCR_XON_ANY 0x20 /* Enable any char to exit XOFF mode */
+
+#define MOS7840_MSR_CTS 0x10 /* Current state of CTS */
+#define MOS7840_MSR_DSR 0x20 /* Current state of DSR */
+#define MOS7840_MSR_RI 0x40 /* Current state of RI */
+#define MOS7840_MSR_CD 0x80 /* Current state of CD */
+
+/*
+ * Defines used for sending commands to port
+ */
+
+#define WAIT_FOR_EVER (HZ * 0 ) /* timeout urb is wait for ever */
+#define MOS_WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */
+
+#define MOS_PORT1 0x0200
+#define MOS_PORT2 0x0300
+#define MOS_VENREG 0x0000
+#define MOS_MAX_PORT 0x02
+#define MOS_WRITE 0x0E
+#define MOS_READ 0x0D
+
+/* Requests */
+#define MCS_RD_RTYPE 0xC0
+#define MCS_WR_RTYPE 0x40
+#define MCS_RDREQ 0x0D
+#define MCS_WRREQ 0x0E
+#define MCS_CTRL_TIMEOUT 500
+#define VENDOR_READ_LENGTH (0x01)
+
+#define MAX_NAME_LEN 64
+
+#define ZLP_REG1 0x3A //Zero_Flag_Reg1 58
+#define ZLP_REG5 0x3E //Zero_Flag_Reg5 62
+
+/* For higher baud Rates use TIOCEXBAUD */
+#define TIOCEXBAUD 0x5462
+
+/* vendor id and device id defines */
+
+#define USB_VENDOR_ID_MOSCHIP 0x9710
+#define MOSCHIP_DEVICE_ID_7840 0x7840
+#define MOSCHIP_DEVICE_ID_7820 0x7820
+
+/* Interrupt Rotinue Defines */
+
+#define SERIAL_IIR_RLS 0x06
+#define SERIAL_IIR_MS 0x00
+
+/*
+ * Emulation of the bit mask on the LINE STATUS REGISTER.
+ */
+#define SERIAL_LSR_DR 0x0001
+#define SERIAL_LSR_OE 0x0002
+#define SERIAL_LSR_PE 0x0004
+#define SERIAL_LSR_FE 0x0008
+#define SERIAL_LSR_BI 0x0010
+
+#define MOS_MSR_DELTA_CTS 0x10
+#define MOS_MSR_DELTA_DSR 0x20
+#define MOS_MSR_DELTA_RI 0x40
+#define MOS_MSR_DELTA_CD 0x80
+
+// Serial Port register Address
+#define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01))
+#define FIFO_CONTROL_REGISTER ((__u16)(0x02))
+#define LINE_CONTROL_REGISTER ((__u16)(0x03))
+#define MODEM_CONTROL_REGISTER ((__u16)(0x04))
+#define LINE_STATUS_REGISTER ((__u16)(0x05))
+#define MODEM_STATUS_REGISTER ((__u16)(0x06))
+#define SCRATCH_PAD_REGISTER ((__u16)(0x07))
+#define DIVISOR_LATCH_LSB ((__u16)(0x00))
+#define DIVISOR_LATCH_MSB ((__u16)(0x01))
+
+#define CLK_MULTI_REGISTER ((__u16)(0x02))
+#define CLK_START_VALUE_REGISTER ((__u16)(0x03))
+
+#define SERIAL_LCR_DLAB ((__u16)(0x0080))
+
+/*
+ * URB POOL related defines
+ */
+#define NUM_URBS 16 /* URB Count */
+#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */
+
+
+static struct usb_device_id moschip_port_id_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
+ {} /* terminating entry */
+};
+
+static __devinitdata struct usb_device_id moschip_id_table_combined[] = {
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
+ {} /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, moschip_id_table_combined);
+
+/* This structure holds all of the local port information */
+
+struct moschip_port {
+ int port_num; /*Actual port number in the device(1,2,etc) */
+ struct urb *write_urb; /* write URB for this port */
+ struct urb *read_urb; /* read URB for this port */
+ __u8 shadowLCR; /* last LCR value received */
+ __u8 shadowMCR; /* last MCR value received */
+ char open;
+ wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */
+ wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */
+ int delta_msr_cond;
+ struct async_icount icount;
+ struct usb_serial_port *port; /* loop back to the owner of this object */
+
+ /*Offsets */
+ __u8 SpRegOffset;
+ __u8 ControlRegOffset;
+ __u8 DcrRegOffset;
+ //for processing control URBS in interrupt context
+ struct urb *control_urb;
+ char *ctrl_buf;
+ int MsrLsr;
+
+ struct urb *write_urb_pool[NUM_URBS];
+};
+
+
+static int debug;
+static int mos7840_num_ports; //this says the number of ports in the device
+static int mos7840_num_open_ports;
+
+
+/*
+ * mos7840_set_reg_sync
+ * To set the Control register by calling usb_fill_control_urb function
+ * by passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
+ __u16 val)
+{
+ struct usb_device *dev = port->serial->dev;
+ val = val & 0x00ff;
+ dbg("mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
+
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+ MCS_WR_RTYPE, val, reg, NULL, 0,
+ MOS_WDR_TIMEOUT);
+}
+
+/*
+ * mos7840_get_reg_sync
+ * To set the Uart register by calling usb_fill_control_urb function by
+ * passing usb_rcvctrlpipe function as parameter.
+ */
+
+static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
+ __u16 * val)
+{
+ struct usb_device *dev = port->serial->dev;
+ int ret = 0;
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+ MCS_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH,
+ MOS_WDR_TIMEOUT);
+ dbg("mos7840_get_reg_sync offset is %x, return val %x\n", reg, *val);
+ *val = (*val) & 0x00ff;
+ return ret;
+}
+
+/*
+ * mos7840_set_uart_reg
+ * To set the Uart register by calling usb_fill_control_urb function by
+ * passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
+ __u16 val)
+{
+
+ struct usb_device *dev = port->serial->dev;
+ val = val & 0x00ff;
+ // For the UART control registers, the application number need to be Or'ed
+ if (mos7840_num_ports == 4) {
+ val |=
+ (((__u16) port->number - (__u16) (port->serial->minor)) +
+ 1) << 8;
+ dbg("mos7840_set_uart_reg application number is %x\n", val);
+ } else {
+ if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+ val |=
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 1) << 8;
+ dbg("mos7840_set_uart_reg application number is %x\n",
+ val);
+ } else {
+ val |=
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 2) << 8;
+ dbg("mos7840_set_uart_reg application number is %x\n",
+ val);
+ }
+ }
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+ MCS_WR_RTYPE, val, reg, NULL, 0,
+ MOS_WDR_TIMEOUT);
+
+}
+
+/*
+ * mos7840_get_uart_reg
+ * To set the Control register by calling usb_fill_control_urb function
+ * by passing usb_rcvctrlpipe function as parameter.
+ */
+static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
+ __u16 * val)
+{
+ struct usb_device *dev = port->serial->dev;
+ int ret = 0;
+ __u16 Wval;
+
+ //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8);
+ /*Wval is same as application number */
+ if (mos7840_num_ports == 4) {
+ Wval =
+ (((__u16) port->number - (__u16) (port->serial->minor)) +
+ 1) << 8;
+ dbg("mos7840_get_uart_reg application number is %x\n", Wval);
+ } else {
+ if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+ Wval =
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 1) << 8;
+ dbg("mos7840_get_uart_reg application number is %x\n",
+ Wval);
+ } else {
+ Wval =
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 2) << 8;
+ dbg("mos7840_get_uart_reg application number is %x\n",
+ Wval);
+ }
+ }
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+ MCS_RD_RTYPE, Wval, reg, val, VENDOR_READ_LENGTH,
+ MOS_WDR_TIMEOUT);
+ *val = (*val) & 0x00ff;
+ return ret;
+}
+
+static void mos7840_dump_serial_port(struct moschip_port *mos7840_port)
+{
+
+ dbg("***************************************\n");
+ dbg("SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
+ dbg("ControlRegOffset is %2x \n", mos7840_port->ControlRegOffset);
+ dbg("DCRRegOffset is %2x \n", mos7840_port->DcrRegOffset);
+ dbg("***************************************\n");
+
+}
+
+/************************************************************************/
+/************************************************************************/
+/* I N T E R F A C E F U N C T I O N S */
+/* I N T E R F A C E F U N C T I O N S */
+/************************************************************************/
+/************************************************************************/
+
+static inline void mos7840_set_port_private(struct usb_serial_port *port,
+ struct moschip_port *data)
+{
+ usb_set_serial_port_data(port, (void *)data);
+}
+
+static inline struct moschip_port *mos7840_get_port_private(struct
+ usb_serial_port
+ *port)
+{
+ return (struct moschip_port *)usb_get_serial_port_data(port);
+}
+
+static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
+{
+ struct moschip_port *mos7840_port;
+ struct async_icount *icount;
+ mos7840_port = port;
+ icount = &mos7840_port->icount;
+ if (new_msr &
+ (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI |
+ MOS_MSR_DELTA_CD)) {
+ icount = &mos7840_port->icount;
+
+ /* update input line counters */
+ if (new_msr & MOS_MSR_DELTA_CTS) {
+ icount->cts++;
+ }
+ if (new_msr & MOS_MSR_DELTA_DSR) {
+ icount->dsr++;
+ }
+ if (new_msr & MOS_MSR_DELTA_CD) {
+ icount->dcd++;
+ }
+ if (new_msr & MOS_MSR_DELTA_RI) {
+ icount->rng++;
+ }
+ }
+
+ return 0;
+}
+
+static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+{
+ struct async_icount *icount;
+
+ dbg("%s - %02x", __FUNCTION__, new_lsr);
+
+ if (new_lsr & SERIAL_LSR_BI) {
+ //
+ // Parity and Framing errors only count if they
+ // occur exclusive of a break being
+ // received.
+ //
+ new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI);
+ }
+
+ /* update input line counters */
+ icount = &port->icount;
+ if (new_lsr & SERIAL_LSR_BI) {
+ icount->brk++;
+ }
+ if (new_lsr & SERIAL_LSR_OE) {
+ icount->overrun++;
+ }
+ if (new_lsr & SERIAL_LSR_PE) {
+ icount->parity++;
+ }
+ if (new_lsr & SERIAL_LSR_FE) {
+ icount->frame++;
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/************************************************************************/
+/* U S B C A L L B A C K F U N C T I O N S */
+/* U S B C A L L B A C K F U N C T I O N S */
+/************************************************************************/
+/************************************************************************/
+
+static void mos7840_control_callback(struct urb *urb, struct pt_regs *regs)
+{
+ unsigned char *data;
+ struct moschip_port *mos7840_port;
+ __u8 regval = 0x0;
+
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+ urb->status);
+ goto exit;
+ }
+
+ mos7840_port = (struct moschip_port *)urb->context;
+
+ dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length);
+ dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__,
+ mos7840_port->MsrLsr, mos7840_port->port_num);
+ data = urb->transfer_buffer;
+ regval = (__u8) data[0];
+ dbg("%s data is %x\n", __FUNCTION__, regval);
+ if (mos7840_port->MsrLsr == 0)
+ mos7840_handle_new_msr(mos7840_port, regval);
+ else if (mos7840_port->MsrLsr == 1)
+ mos7840_handle_new_lsr(mos7840_port, regval);
+
+ exit:
+ return;
+}
+
+static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
+ __u16 * val)
+{
+ struct usb_device *dev = mcs->port->serial->dev;
+ struct usb_ctrlrequest *dr = NULL;
+ unsigned char *buffer = NULL;
+ int ret = 0;
+ buffer = (__u8 *) mcs->ctrl_buf;
+
+// dr=(struct usb_ctrlrequest *)(buffer);
+ dr = (void *)(buffer + 2);
+ dr->bRequestType = MCS_RD_RTYPE;
+ dr->bRequest = MCS_RDREQ;
+ dr->wValue = cpu_to_le16(Wval); //0;
+ dr->wIndex = cpu_to_le16(reg);
+ dr->wLength = cpu_to_le16(2);
+
+ usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0),
+ (unsigned char *)dr, buffer, 2,
+ mos7840_control_callback, mcs);
+ mcs->control_urb->transfer_buffer_length = 2;
+ ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
+ return ret;
+}
+
+/*****************************************************************************
+ * mos7840_interrupt_callback
+ * this is the callback function for when we have received data on the
+ * interrupt endpoint.
+ *****************************************************************************/
+
+static void mos7840_interrupt_callback(struct urb *urb, struct pt_regs *regs)
+{
+ int result;
+ int length;
+ struct moschip_port *mos7840_port;
+ struct usb_serial *serial;
+ __u16 Data;
+ unsigned char *data;
+ __u8 sp[5], st;
+ int i;
+ __u16 wval;
+
+ dbg("%s", " : Entering\n");
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+ urb->status);
+ goto exit;
+ }
+
+ length = urb->actual_length;
+ data = urb->transfer_buffer;
+
+ serial = (struct usb_serial *)urb->context;
+
+ /* Moschip get 5 bytes
+ * Byte 1 IIR Port 1 (port.number is 0)
+ * Byte 2 IIR Port 2 (port.number is 1)
+ * Byte 3 IIR Port 3 (port.number is 2)
+ * Byte 4 IIR Port 4 (port.number is 3)
+ * Byte 5 FIFO status for both */
+
+ if (length && length > 5) {
+ dbg("%s \n", "Wrong data !!!");
+ return;
+ }
+
+ sp[0] = (__u8) data[0];
+ sp[1] = (__u8) data[1];
+ sp[2] = (__u8) data[2];
+ sp[3] = (__u8) data[3];
+ st = (__u8) data[4];
+
+ for (i = 0; i < serial->num_ports; i++) {
+ mos7840_port = mos7840_get_port_private(serial->port[i]);
+ wval =
+ (((__u16) serial->port[i]->number -
+ (__u16) (serial->minor)) + 1) << 8;
+ if (mos7840_port->open) {
+ if (sp[i] & 0x01) {
+ dbg("SP%d No Interrupt !!!\n", i);
+ } else {
+ switch (sp[i] & 0x0f) {
+ case SERIAL_IIR_RLS:
+ dbg("Serial Port %d: Receiver status error or ", i);
+ dbg("address bit detected in 9-bit mode\n");
+ mos7840_port->MsrLsr = 1;
+ mos7840_get_reg(mos7840_port, wval,
+ LINE_STATUS_REGISTER,
+ &Data);
+ break;
+ case SERIAL_IIR_MS:
+ dbg("Serial Port %d: Modem status change\n", i);
+ mos7840_port->MsrLsr = 0;
+ mos7840_get_reg(mos7840_port, wval,
+ MODEM_STATUS_REGISTER,
+ &Data);
+ break;
+ }
+ }
+ }
+ }
+ exit:
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result) {
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __FUNCTION__, result);
+ }
+
+ return;
+
+}
+
+static int mos7840_port_paranoia_check(struct usb_serial_port *port,
+ const char *function)
+{
+ if (!port) {
+ dbg("%s - port == NULL", function);
+ return -1;
+ }
+ if (!port->serial) {
+ dbg("%s - port->serial == NULL", function);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static int mos7840_serial_paranoia_check(struct usb_serial *serial,
+ const char *function)
+{
+ if (!serial) {
+ dbg("%s - serial == NULL", function);
+ return -1;
+ }
+ if (!serial->type) {
+ dbg("%s - serial->type == NULL!", function);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
+ const char *function)
+{
+ /* if no port was specified, or it fails a paranoia check */
+ if (!port ||
+ mos7840_port_paranoia_check(port, function) ||
+ mos7840_serial_paranoia_check(port->serial, function)) {
+ /* then say that we don't have a valid usb_serial thing, which will * end up genrating -ENODEV return values */
+ return NULL;
+ }
+
+ return port->serial;
+}
+
+/*****************************************************************************
+ * mos7840_bulk_in_callback
+ * this is the callback function for when we have received data on the
+ * bulk in endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_in_callback(struct urb *urb, struct pt_regs *regs)
+{
+ int status;
+ unsigned char *data;
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ if (urb->status) {
+ dbg("nonzero read bulk status received: %d", urb->status);
+ return;
+ }
+
+ mos7840_port = (struct moschip_port *)urb->context;
+ if (!mos7840_port) {
+ dbg("%s", "NULL mos7840_port pointer \n");
+ return;
+ }
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ serial = mos7840_get_usb_serial(port, __FUNCTION__);
+ if (!serial) {
+ dbg("%s\n", "Bad serial pointer ");
+ return;
+ }
+
+ dbg("%s\n", "Entering... \n");
+
+ data = urb->transfer_buffer;
+
+ dbg("%s", "Entering ........... \n");
+
+ if (urb->actual_length) {
+ tty = mos7840_port->port->tty;
+ if (tty) {
+ tty_buffer_request_room(tty, urb->actual_length);
+ tty_insert_flip_string(tty, data, urb->actual_length);
+ dbg(" %s \n", data);
+ tty_flip_buffer_push(tty);
+ }
+ mos7840_port->icount.rx += urb->actual_length;
+ dbg("mos7840_port->icount.rx is %d:\n",
+ mos7840_port->icount.rx);
+ }
+
+ if (!mos7840_port->read_urb) {
+ dbg("%s", "URB KILLED !!!\n");
+ return;
+ }
+
+ if (mos7840_port->read_urb->status != -EINPROGRESS) {
+ mos7840_port->read_urb->dev = serial->dev;
+
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
+ }
+ }
+}
+
+/*****************************************************************************
+ * mos7840_bulk_out_data_callback
+ * this is the callback function for when we have finished sending serial data
+ * on the bulk out endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_out_data_callback(struct urb *urb,
+ struct pt_regs *regs)
+{
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ if (urb->status) {
+ dbg("nonzero write bulk status received:%d\n", urb->status);
+ return;
+ }
+
+ mos7840_port = (struct moschip_port *)urb->context;
+ if (!mos7840_port) {
+ dbg("%s", "NULL mos7840_port pointer \n");
+ return;
+ }
+
+ if (mos7840_port_paranoia_check(mos7840_port->port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ dbg("%s \n", "Entering .........");
+
+ tty = mos7840_port->port->tty;
+
+ if (tty && mos7840_port->open) {
+ /* let the tty driver wakeup if it has a special *
+ * write_wakeup function */
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup) {
+ (tty->ldisc.write_wakeup) (tty);
+ }
+
+ /* tell the tty driver that something has changed */
+ wake_up_interruptible(&tty->write_wait);
+ }
+
+}
+
+/************************************************************************/
+/* D R I V E R T T Y I N T E R F A C E F U N C T I O N S */
+/************************************************************************/
+#ifdef MCSSerialProbe
+static int mos7840_serial_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+{
+
+ /*need to implement the mode_reg reading and updating\
+ structures usb_serial_ device_type\
+ (i.e num_ports, num_bulkin,bulkout etc) */
+ /* Also we can update the changes attach */
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * mos7840_open
+ * this function is called by the tty driver when a port is opened
+ * If successful, we return 0
+ * Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_open(struct usb_serial_port *port, struct file *filp)
+{
+ int response;
+ int j;
+ struct usb_serial *serial;
+ struct urb *urb;
+ __u16 Data;
+ int status;
+ struct moschip_port *mos7840_port;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return -ENODEV;
+ }
+
+ mos7840_num_open_ports++;
+ serial = port->serial;
+
+ if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return -ENODEV;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return -ENODEV;
+
+ usb_clear_halt(serial->dev, port->write_urb->pipe);
+ usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+ /* Initialising the write urb pool */
+ for (j = 0; j < NUM_URBS; ++j) {
+ urb = usb_alloc_urb(0, SLAB_ATOMIC);
+ mos7840_port->write_urb_pool[j] = urb;
+
+ if (urb == NULL) {
+ err("No more urbs???");
+ continue;
+ }
+
+ urb->transfer_buffer = NULL;
+ urb->transfer_buffer =
+ kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+ if (!urb->transfer_buffer) {
+ err("%s-out of memory for urb buffers.", __FUNCTION__);
+ continue;
+ }
+ }
+
+/*****************************************************************************
+ * Initialize MCS7840 -- Write Init values to corresponding Registers
+ *
+ * Register Index
+ * 1 : IER
+ * 2 : FCR
+ * 3 : LCR
+ * 4 : MCR
+ *
+ * 0x08 : SP1/2 Control Reg
+ *****************************************************************************/
+
+//NEED to check the following Block
+
+ status = 0;
+ Data = 0x0;
+ status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+ if (status < 0) {
+ dbg("Reading Spreg failed\n");
+ return -1;
+ }
+ Data |= 0x80;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ if (status < 0) {
+ dbg("writing Spreg failed\n");
+ return -1;
+ }
+
+ Data &= ~0x80;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ if (status < 0) {
+ dbg("writing Spreg failed\n");
+ return -1;
+ }
+//End of block to be checked
+
+ status = 0;
+ Data = 0x0;
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+ if (status < 0) {
+ dbg("Reading Controlreg failed\n");
+ return -1;
+ }
+ Data |= 0x08; //Driver done bit
+ Data |= 0x20; //rx_disable
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+ if (status < 0) {
+ dbg("writing Controlreg failed\n");
+ return -1;
+ }
+ //do register settings here
+ // Set all regs to the device default values.
+ ////////////////////////////////////
+ // First Disable all interrupts.
+ ////////////////////////////////////
+
+ Data = 0x00;
+ status = 0;
+ status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+ if (status < 0) {
+ dbg("disableing interrupts failed\n");
+ return -1;
+ }
+ // Set FIFO_CONTROL_REGISTER to the default value
+ Data = 0x00;
+ status = 0;
+ status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing FIFO_CONTROL_REGISTER failed\n");
+ return -1;
+ }
+
+ Data = 0xcf;
+ status = 0;
+ status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing FIFO_CONTROL_REGISTER failed\n");
+ return -1;
+ }
+
+ Data = 0x03;
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+ mos7840_port->shadowLCR = Data;
+
+ Data = 0x0b;
+ status = 0;
+ status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ mos7840_port->shadowMCR = Data;
+
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+ mos7840_port->shadowLCR = Data;
+
+ Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ Data = 0x0c;
+ status = 0;
+ status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+ Data = 0x0;
+ status = 0;
+ status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+
+ Data = Data & ~SERIAL_LCR_DLAB;
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+ mos7840_port->shadowLCR = Data;
+
+ //clearing Bulkin and Bulkout Fifo
+ Data = 0x0;
+ status = 0;
+ status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+
+ Data = Data | 0x0c;
+ status = 0;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+
+ Data = Data & ~0x0c;
+ status = 0;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ //Finally enable all interrupts
+ Data = 0x0;
+ Data = 0x0c;
+ status = 0;
+ status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ //clearing rx_disable
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+ Data = Data & ~0x20;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+
+ // rx_negate
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+ Data = Data | 0x10;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+
+ /* force low_latency on so that our tty_push actually forces *
+ * the data through,otherwise it is scheduled, and with *
+ * high data rates (like with OHCI) data can get lost. */
+
+ if (port->tty)
+ port->tty->low_latency = 1;
+/* Check to see if we've set up our endpoint info yet *
+ * (can't set it up in mos7840_startup as the structures *
+ * were not set up at that time.) */
+ if (mos7840_num_open_ports == 1) {
+ if (serial->port[0]->interrupt_in_buffer == NULL) {
+
+ /* set up interrupt urb */
+
+ usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
+ serial->dev,
+ usb_rcvintpipe(serial->dev,
+ serial->port[0]->
+ interrupt_in_endpointAddress),
+ serial->port[0]->interrupt_in_buffer,
+ serial->port[0]->interrupt_in_urb->
+ transfer_buffer_length,
+ mos7840_interrupt_callback,
+ serial,
+ serial->port[0]->interrupt_in_urb->
+ interval);
+
+ /* start interrupt read for mos7840 *
+ * will continue as long as mos7840 is connected */
+
+ response =
+ usb_submit_urb(serial->port[0]->interrupt_in_urb,
+ GFP_KERNEL);
+ if (response) {
+ err("%s - Error %d submitting interrupt urb",
+ __FUNCTION__, response);
+ }
+
+ }
+
+ }
+
+ /* see if we've set up our endpoint info yet *
+ * (can't set it up in mos7840_startup as the *
+ * structures were not set up at that time.) */
+
+ dbg("port number is %d \n", port->number);
+ dbg("serial number is %d \n", port->serial->minor);
+ dbg("Bulkin endpoint is %d \n", port->bulk_in_endpointAddress);
+ dbg("BulkOut endpoint is %d \n", port->bulk_out_endpointAddress);
+ dbg("Interrupt endpoint is %d \n", port->interrupt_in_endpointAddress);
+ dbg("port's number in the device is %d\n", mos7840_port->port_num);
+ mos7840_port->read_urb = port->read_urb;
+
+ /* set up our bulk in urb */
+
+ usb_fill_bulk_urb(mos7840_port->read_urb,
+ serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_in_endpointAddress),
+ port->bulk_in_buffer,
+ mos7840_port->read_urb->transfer_buffer_length,
+ mos7840_bulk_in_callback, mos7840_port);
+
+ dbg("mos7840_open: bulkin endpoint is %d\n",
+ port->bulk_in_endpointAddress);
+ response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
+ if (response) {
+ err("%s - Error %d submitting control urb", __FUNCTION__,
+ response);
+ }
+
+ /* initialize our wait queues */
+ init_waitqueue_head(&mos7840_port->wait_chase);
+ init_waitqueue_head(&mos7840_port->delta_msr_wait);
+
+ /* initialize our icount structure */
+ memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount));
+
+ /* initialize our port settings */
+ mos7840_port->shadowMCR = MCR_MASTER_IE; /* Must set to enable ints! */
+ /* send a open port command */
+ mos7840_port->open = 1;
+ //mos7840_change_port_settings(mos7840_port,old_termios);
+ mos7840_port->icount.tx = 0;
+ mos7840_port->icount.rx = 0;
+
+ dbg("\n\nusb_serial serial:%x mos7840_port:%x\n usb_serial_port port:%x\n\n", (unsigned int)serial, (unsigned int)mos7840_port, (unsigned int)port);
+
+ return 0;
+
+}
+
+/*****************************************************************************
+ * mos7840_chars_in_buffer
+ * this function is called by the tty driver when it wants to know how many
+ * bytes of data we currently have outstanding in the port (data that has
+ * been written, but hasn't made it out the port yet)
+ * If successful, we return the number of bytes left to be written in the
+ * system,
+ * Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_chars_in_buffer(struct usb_serial_port *port)
+{
+ int i;
+ int chars = 0;
+ struct moschip_port *mos7840_port;
+
+ dbg("%s \n", " mos7840_chars_in_buffer:entering ...........");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ if (mos7840_port == NULL) {
+ dbg("%s \n", "mos7840_break:leaving ...........");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) {
+ chars += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+ dbg("%s - returns %d", __FUNCTION__, chars);
+ return (chars);
+
+}
+
+/************************************************************************
+ *
+ * mos7840_block_until_tx_empty
+ *
+ * This function will block the close until one of the following:
+ * 1. TX count are 0
+ * 2. The mos7840 has stopped
+ * 3. A timout of 3 seconds without activity has expired
+ *
+ ************************************************************************/
+static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port)
+{
+ int timeout = HZ / 10;
+ int wait = 30;
+ int count;
+
+ while (1) {
+
+ count = mos7840_chars_in_buffer(mos7840_port->port);
+
+ /* Check for Buffer status */
+ if (count <= 0) {
+ return;
+ }
+
+ /* Block the thread for a while */
+ interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
+ timeout);
+
+ /* No activity.. count down section */
+ wait--;
+ if (wait == 0) {
+ dbg("%s - TIMEOUT", __FUNCTION__);
+ return;
+ } else {
+ /* Reset timout value back to seconds */
+ wait = 30;
+ }
+ }
+}
+
+/*****************************************************************************
+ * mos7840_close
+ * this function is called by the tty driver when a port is closed
+ *****************************************************************************/
+
+static void mos7840_close(struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial;
+ struct moschip_port *mos7840_port;
+ int j;
+ __u16 Data;
+
+ dbg("%s\n", "mos7840_close:entering...");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ serial = mos7840_get_usb_serial(port, __FUNCTION__);
+ if (!serial) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL) {
+ return;
+ }
+
+ for (j = 0; j < NUM_URBS; ++j)
+ usb_kill_urb(mos7840_port->write_urb_pool[j]);
+
+ /* Freeing Write URBs */
+ for (j = 0; j < NUM_URBS; ++j) {
+ if (mos7840_port->write_urb_pool[j]) {
+ if (mos7840_port->write_urb_pool[j]->transfer_buffer)
+ kfree(mos7840_port->write_urb_pool[j]->
+ transfer_buffer);
+
+ usb_free_urb(mos7840_port->write_urb_pool[j]);
+ }
+ }
+
+ if (serial->dev) {
+ /* flush and block until tx is empty */
+ mos7840_block_until_tx_empty(mos7840_port);
+ }
+
+ /* While closing port, shutdown all bulk read, write *
+ * and interrupt read if they exists */
+ if (serial->dev) {
+
+ if (mos7840_port->write_urb) {
+ dbg("%s", "Shutdown bulk write\n");
+ usb_kill_urb(mos7840_port->write_urb);
+ }
+
+ if (mos7840_port->read_urb) {
+ dbg("%s", "Shutdown bulk read\n");
+ usb_kill_urb(mos7840_port->read_urb);
+ }
+ if ((&mos7840_port->control_urb)) {
+ dbg("%s", "Shutdown control read\n");
+ // usb_kill_urb (mos7840_port->control_urb);
+
+ }
+ }
+// if(mos7840_port->ctrl_buf != NULL)
+// kfree(mos7840_port->ctrl_buf);
+ mos7840_num_open_ports--;
+ dbg("mos7840_num_open_ports in close%d:in port%d\n",
+ mos7840_num_open_ports, port->number);
+ if (mos7840_num_open_ports == 0) {
+ if (serial->port[0]->interrupt_in_urb) {
+ dbg("%s", "Shutdown interrupt_in_urb\n");
+ }
+ }
+
+ if (mos7840_port->write_urb) {
+ /* if this urb had a transfer buffer already (old tx) free it */
+
+ if (mos7840_port->write_urb->transfer_buffer != NULL) {
+ kfree(mos7840_port->write_urb->transfer_buffer);
+ }
+ usb_free_urb(mos7840_port->write_urb);
+ }
+
+ Data = 0x0;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+ Data = 0x00;
+ mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ mos7840_port->open = 0;
+
+ dbg("%s \n", "Leaving ............");
+}
+
+/************************************************************************
+ *
+ * mos7840_block_until_chase_response
+ *
+ * This function will block the close until one of the following:
+ * 1. Response to our Chase comes from mos7840
+ * 2. A timout of 10 seconds without activity has expired
+ * (1K of mos7840 data @ 2400 baud ==> 4 sec to empty)
+ *
+ ************************************************************************/
+
+static void mos7840_block_until_chase_response(struct moschip_port
+ *mos7840_port)
+{
+ int timeout = 1 * HZ;
+ int wait = 10;
+ int count;
+
+ while (1) {
+ count = mos7840_chars_in_buffer(mos7840_port->port);
+
+ /* Check for Buffer status */
+ if (count <= 0) {
+ return;
+ }
+
+ /* Block the thread for a while */
+ interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
+ timeout);
+ /* No activity.. count down section */
+ wait--;
+ if (wait == 0) {
+ dbg("%s - TIMEOUT", __FUNCTION__);
+ return;
+ } else {
+ /* Reset timout value back to seconds */
+ wait = 10;
+ }
+ }
+
+}
+
+/*****************************************************************************
+ * mos7840_break
+ * this function sends a break to the port
+ *****************************************************************************/
+static void mos7840_break(struct usb_serial_port *port, int break_state)
+{
+ unsigned char data;
+ struct usb_serial *serial;
+ struct moschip_port *mos7840_port;
+
+ dbg("%s \n", "Entering ...........");
+ dbg("mos7840_break: Start\n");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ serial = mos7840_get_usb_serial(port, __FUNCTION__);
+ if (!serial) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL) {
+ return;
+ }
+
+ if (serial->dev) {
+
+ /* flush and block until tx is empty */
+ mos7840_block_until_chase_response(mos7840_port);
+ }
+
+ if (break_state == -1) {
+ data = mos7840_port->shadowLCR | LCR_SET_BREAK;
+ } else {
+ data = mos7840_port->shadowLCR & ~LCR_SET_BREAK;
+ }
+
+ mos7840_port->shadowLCR = data;
+ dbg("mcs7840_break mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
+ mos7840_port->shadowLCR);
+
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_write_room
+ * this function is called by the tty driver when it wants to know how many
+ * bytes of data we can accept for a specific port.
+ * If successful, we return the amount of room that we have for this port
+ * Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write_room(struct usb_serial_port *port)
+{
+ int i;
+ int room = 0;
+ struct moschip_port *mos7840_port;
+
+ dbg("%s \n", " mos7840_write_room:entering ...........");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ dbg("%s \n", " mos7840_write_room:leaving ...........");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ if (mos7840_port == NULL) {
+ dbg("%s \n", "mos7840_break:leaving ...........");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ room += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ dbg("%s - returns %d", __FUNCTION__, room);
+ return (room);
+
+}
+
+/*****************************************************************************
+ * mos7840_write
+ * this function is called by the tty driver when data should be written to
+ * the port.
+ * If successful, we return the number of bytes written, otherwise we
+ * return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write(struct usb_serial_port *port,
+ const unsigned char *data, int count)
+{
+ int status;
+ int i;
+ int bytes_sent = 0;
+ int transfer_size;
+ int from_user = 0;
+
+ struct moschip_port *mos7840_port;
+ struct usb_serial *serial;
+ struct urb *urb;
+ //__u16 Data;
+ const unsigned char *current_position = data;
+ unsigned char *data1;
+ dbg("%s \n", "entering ...........");
+ //dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",mos7840_port->shadowLCR);
+
+#ifdef NOTMOS7840
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+ mos7840_port->shadowLCR = Data;
+ dbg("mos7840_write: LINE_CONTROL_REGISTER is %x\n", Data);
+ dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+
+ //Data = 0x03;
+ //status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data);
+ //mos7840_port->shadowLCR=Data;//Need to add later
+
+ Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ //Data = 0x0c;
+ //status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data);
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data);
+ dbg("mos7840_write:DLL value is %x\n", Data);
+
+ Data = 0x0;
+ status = 0;
+ status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data);
+ dbg("mos7840_write:DLM value is %x\n", Data);
+
+ Data = Data & ~SERIAL_LCR_DLAB;
+ dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+#endif
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return -1;
+ }
+
+ serial = port->serial;
+ if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ if (mos7840_port == NULL) {
+ dbg("%s", "mos7840_port is NULL\n");
+ return -1;
+ }
+
+ /* try to find a free urb in the list */
+ urb = NULL;
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ urb = mos7840_port->write_urb_pool[i];
+ dbg("\nURB:%d", i);
+ break;
+ }
+ }
+
+ if (urb == NULL) {
+ dbg("%s - no more free urbs", __FUNCTION__);
+ goto exit;
+ }
+
+ if (urb->transfer_buffer == NULL) {
+ urb->transfer_buffer =
+ kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+
+ if (urb->transfer_buffer == NULL) {
+ err("%s no more kernel memory...", __FUNCTION__);
+ goto exit;
+ }
+ }
+ transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
+
+ if (from_user) {
+ if (copy_from_user
+ (urb->transfer_buffer, current_position, transfer_size)) {
+ bytes_sent = -EFAULT;
+ goto exit;
+ }
+ } else {
+ memcpy(urb->transfer_buffer, current_position, transfer_size);
+ }
+
+ /* fill urb with data and submit */
+ usb_fill_bulk_urb(urb,
+ serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ urb->transfer_buffer,
+ transfer_size,
+ mos7840_bulk_out_data_callback, mos7840_port);
+
+ data1 = urb->transfer_buffer;
+ dbg("\nbulkout endpoint is %d", port->bulk_out_endpointAddress);
+
+ /* send it down the pipe */
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (status) {
+ err("%s - usb_submit_urb(write bulk) failed with status = %d",
+ __FUNCTION__, status);
+ bytes_sent = status;
+ goto exit;
+ }
+ bytes_sent = transfer_size;
+ mos7840_port->icount.tx += transfer_size;
+ dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
+ exit:
+
+ return bytes_sent;
+
+}
+
+/*****************************************************************************
+ * mos7840_throttle
+ * this function is called by the tty driver when it wants to stop the data
+ * being read from the port.
+ *****************************************************************************/
+
+static void mos7840_throttle(struct usb_serial_port *port)
+{
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+ int status;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ dbg("- port %d\n", port->number);
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return;
+
+ if (!mos7840_port->open) {
+ dbg("%s\n", "port not opened");
+ return;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ tty = port->tty;
+ if (!tty) {
+ dbg("%s - no tty available", __FUNCTION__);
+ return;
+ }
+
+ /* if we are implementing XON/XOFF, send the stop character */
+ if (I_IXOFF(tty)) {
+ unsigned char stop_char = STOP_CHAR(tty);
+ status = mos7840_write(port, &stop_char, 1);
+ if (status <= 0) {
+ return;
+ }
+ }
+
+ /* if we are implementing RTS/CTS, toggle that line */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ mos7840_port->shadowMCR &= ~MCR_RTS;
+ status = 0;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+ mos7840_port->shadowMCR);
+
+ if (status < 0) {
+ return;
+ }
+ }
+
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_unthrottle
+ * this function is called by the tty driver when it wants to resume the data
+ * being read from the port (called after SerialThrottle is called)
+ *****************************************************************************/
+static void mos7840_unthrottle(struct usb_serial_port *port)
+{
+ struct tty_struct *tty;
+ int status;
+ struct moschip_port *mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ if (mos7840_port == NULL)
+ return;
+
+ if (!mos7840_port->open) {
+ dbg("%s - port not opened", __FUNCTION__);
+ return;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ tty = port->tty;
+ if (!tty) {
+ dbg("%s - no tty available", __FUNCTION__);
+ return;
+ }
+
+ /* if we are implementing XON/XOFF, send the start character */
+ if (I_IXOFF(tty)) {
+ unsigned char start_char = START_CHAR(tty);
+ status = mos7840_write(port, &start_char, 1);
+ if (status <= 0) {
+ return;
+ }
+ }
+
+ /* if we are implementing RTS/CTS, toggle that line */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ mos7840_port->shadowMCR |= MCR_RTS;
+ status = 0;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+ mos7840_port->shadowMCR);
+ if (status < 0) {
+ return;
+ }
+ }
+
+ return;
+}
+
+static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+ struct moschip_port *mos7840_port;
+ unsigned int result;
+ __u16 msr;
+ __u16 mcr;
+ int status = 0;
+ mos7840_port = mos7840_get_port_private(port);
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (mos7840_port == NULL)
+ return -ENODEV;
+
+ status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+ status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+ result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
+ | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
+ | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
+ | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0)
+ | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0)
+ | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)
+ | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0);
+
+ dbg("%s - 0x%04X", __FUNCTION__, result);
+
+ return result;
+}
+
+static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct moschip_port *mos7840_port;
+ unsigned int mcr;
+ unsigned int status;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return -ENODEV;
+
+ mcr = mos7840_port->shadowMCR;
+ if (clear & TIOCM_RTS)
+ mcr &= ~MCR_RTS;
+ if (clear & TIOCM_DTR)
+ mcr &= ~MCR_DTR;
+ if (clear & TIOCM_LOOP)
+ mcr &= ~MCR_LOOPBACK;
+
+ if (set & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ if (set & TIOCM_DTR)
+ mcr |= MCR_DTR;
+ if (set & TIOCM_LOOP)
+ mcr |= MCR_LOOPBACK;
+
+ mos7840_port->shadowMCR = mcr;
+
+ status = 0;
+ status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
+ if (status < 0) {
+ dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_calc_baud_rate_divisor
+ * this function calculates the proper baud rate divisor for the specified
+ * baud rate.
+ *****************************************************************************/
+static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor,
+ __u16 * clk_sel_val)
+{
+
+ dbg("%s - %d", __FUNCTION__, baudRate);
+
+ if (baudRate <= 115200) {
+ *divisor = 115200 / baudRate;
+ *clk_sel_val = 0x0;
+ }
+ if ((baudRate > 115200) && (baudRate <= 230400)) {
+ *divisor = 230400 / baudRate;
+ *clk_sel_val = 0x10;
+ } else if ((baudRate > 230400) && (baudRate <= 403200)) {
+ *divisor = 403200 / baudRate;
+ *clk_sel_val = 0x20;
+ } else if ((baudRate > 403200) && (baudRate <= 460800)) {
+ *divisor = 460800 / baudRate;
+ *clk_sel_val = 0x30;
+ } else if ((baudRate > 460800) && (baudRate <= 806400)) {
+ *divisor = 806400 / baudRate;
+ *clk_sel_val = 0x40;
+ } else if ((baudRate > 806400) && (baudRate <= 921600)) {
+ *divisor = 921600 / baudRate;
+ *clk_sel_val = 0x50;
+ } else if ((baudRate > 921600) && (baudRate <= 1572864)) {
+ *divisor = 1572864 / baudRate;
+ *clk_sel_val = 0x60;
+ } else if ((baudRate > 1572864) && (baudRate <= 3145728)) {
+ *divisor = 3145728 / baudRate;
+ *clk_sel_val = 0x70;
+ }
+ return 0;
+
+#ifdef NOTMCS7840
+
+ for (i = 0; i < ARRAY_SIZE(mos7840_divisor_table); i++) {
+ if (mos7840_divisor_table[i].BaudRate == baudrate) {
+ *divisor = mos7840_divisor_table[i].Divisor;
+ return 0;
+ }
+ }
+
+ /* After trying for all the standard baud rates *
+ * Try calculating the divisor for this baud rate */
+
+ if (baudrate > 75 && baudrate < 230400) {
+ /* get the divisor */
+ custom = (__u16) (230400L / baudrate);
+
+ /* Check for round off */
+ round1 = (__u16) (2304000L / baudrate);
+ round = (__u16) (round1 - (custom * 10));
+ if (round > 4) {
+ custom++;
+ }
+ *divisor = custom;
+
+ dbg(" Baud %d = %d\n", baudrate, custom);
+ return 0;
+ }
+
+ dbg("%s\n", " Baud calculation Failed...");
+ return -1;
+#endif
+}
+
+/*****************************************************************************
+ * mos7840_send_cmd_write_baud_rate
+ * this function sends the proper command to change the baud rate of the
+ * specified port.
+ *****************************************************************************/
+
+static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
+ int baudRate)
+{
+ int divisor = 0;
+ int status;
+ __u16 Data;
+ unsigned char number;
+ __u16 clk_sel_val;
+ struct usb_serial_port *port;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) {
+ dbg("%s", "Invalid Serial \n");
+ return -1;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ number = mos7840_port->port->number - mos7840_port->port->serial->minor;
+
+ dbg("%s - port = %d, baud = %d", __FUNCTION__,
+ mos7840_port->port->number, baudRate);
+ //reset clk_uart_sel in spregOffset
+ if (baudRate > 115200) {
+#ifdef HW_flow_control
+ //NOTE: need to see the pther register to modify
+ //setting h/w flow control bit to 1;
+ status = 0;
+ Data = 0x2b;
+ mos7840_port->shadowMCR = Data;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing spreg failed in set_serial_baud\n");
+ return -1;
+ }
+#endif
+
+ } else {
+#ifdef HW_flow_control
+ //setting h/w flow control bit to 0;
+ status = 0;
+ Data = 0xb;
+ mos7840_port->shadowMCR = Data;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing spreg failed in set_serial_baud\n");
+ return -1;
+ }
+#endif
+
+ }
+
+ if (1) //baudRate <= 115200)
+ {
+ clk_sel_val = 0x0;
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_calc_baud_rate_divisor(baudRate, &divisor,
+ &clk_sel_val);
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
+ &Data);
+ if (status < 0) {
+ dbg("reading spreg failed in set_serial_baud\n");
+ return -1;
+ }
+ Data = (Data & 0x8f) | clk_sel_val;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ if (status < 0) {
+ dbg("Writing spreg failed in set_serial_baud\n");
+ return -1;
+ }
+ /* Calculate the Divisor */
+
+ if (status) {
+ err("%s - bad baud rate", __FUNCTION__);
+ dbg("%s\n", "bad baud rate");
+ return status;
+ }
+ /* Enable access to divisor latch */
+ Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB;
+ mos7840_port->shadowLCR = Data;
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ /* Write the divisor */
+ Data = (unsigned char)(divisor & 0xff);
+ dbg("set_serial_baud Value to write DLL is %x\n", Data);
+ mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+ Data = (unsigned char)((divisor & 0xff00) >> 8);
+ dbg("set_serial_baud Value to write DLM is %x\n", Data);
+ mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+ /* Disable access to divisor latch */
+ Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB;
+ mos7840_port->shadowLCR = Data;
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ }
+
+ return status;
+}
+
+/*****************************************************************************
+ * mos7840_change_port_settings
+ * This routine is called to set the UART on the device to match
+ * the specified new settings.
+ *****************************************************************************/
+
+static void mos7840_change_port_settings(struct moschip_port *mos7840_port,
+ struct termios *old_termios)
+{
+ struct tty_struct *tty;
+ int baud;
+ unsigned cflag;
+ unsigned iflag;
+ __u8 lData;
+ __u8 lParity;
+ __u8 lStop;
+ int status;
+ __u16 Data;
+ struct usb_serial_port *port;
+ struct usb_serial *serial;
+
+ if (mos7840_port == NULL)
+ return;
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) {
+ dbg("%s", "Invalid Serial \n");
+ return;
+ }
+
+ serial = port->serial;
+
+ dbg("%s - port %d", __FUNCTION__, mos7840_port->port->number);
+
+ if (!mos7840_port->open) {
+ dbg("%s - port not opened", __FUNCTION__);
+ return;
+ }
+
+ tty = mos7840_port->port->tty;
+
+ if ((!tty) || (!tty->termios)) {
+ dbg("%s - no tty structures", __FUNCTION__);
+ return;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ lData = LCR_BITS_8;
+ lStop = LCR_STOP_1;
+ lParity = LCR_PAR_NONE;
+
+ cflag = tty->termios->c_cflag;
+ iflag = tty->termios->c_iflag;
+
+ /* Change the number of bits */
+ if (cflag & CSIZE) {
+ switch (cflag & CSIZE) {
+ case CS5:
+ lData = LCR_BITS_5;
+ break;
+
+ case CS6:
+ lData = LCR_BITS_6;
+ break;
+
+ case CS7:
+ lData = LCR_BITS_7;
+ break;
+ default:
+ case CS8:
+ lData = LCR_BITS_8;
+ break;
+ }
+ }
+ /* Change the Parity bit */
+ if (cflag & PARENB) {
+ if (cflag & PARODD) {
+ lParity = LCR_PAR_ODD;
+ dbg("%s - parity = odd", __FUNCTION__);
+ } else {
+ lParity = LCR_PAR_EVEN;
+ dbg("%s - parity = even", __FUNCTION__);
+ }
+
+ } else {
+ dbg("%s - parity = none", __FUNCTION__);
+ }
+
+ if (cflag & CMSPAR) {
+ lParity = lParity | 0x20;
+ }
+
+ /* Change the Stop bit */
+ if (cflag & CSTOPB) {
+ lStop = LCR_STOP_2;
+ dbg("%s - stop bits = 2", __FUNCTION__);
+ } else {
+ lStop = LCR_STOP_1;
+ dbg("%s - stop bits = 1", __FUNCTION__);
+ }
+
+ /* Update the LCR with the correct value */
+ mos7840_port->shadowLCR &=
+ ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
+ mos7840_port->shadowLCR |= (lData | lParity | lStop);
+
+ dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+ /* Disable Interrupts */
+ Data = 0x00;
+ mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ Data = 0x00;
+ mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+ Data = 0xcf;
+ mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+ /* Send the updated LCR value to the mos7840 */
+ Data = mos7840_port->shadowLCR;
+
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ Data = 0x00b;
+ mos7840_port->shadowMCR = Data;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ Data = 0x00b;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+ /* set up the MCR register and send it to the mos7840 */
+
+ mos7840_port->shadowMCR = MCR_MASTER_IE;
+ if (cflag & CBAUD) {
+ mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS);
+ }
+
+ if (cflag & CRTSCTS) {
+ mos7840_port->shadowMCR |= (MCR_XON_ANY);
+
+ } else {
+ mos7840_port->shadowMCR &= ~(MCR_XON_ANY);
+ }
+
+ Data = mos7840_port->shadowMCR;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+ /* Determine divisor based on baud rate */
+ baud = tty_get_baud_rate(tty);
+
+ if (!baud) {
+ /* pick a default, any default... */
+ dbg("%s\n", "Picked default baud...");
+ baud = 9600;
+ }
+
+ dbg("%s - baud rate = %d", __FUNCTION__, baud);
+ status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud);
+
+ /* Enable Interrupts */
+ Data = 0x0c;
+ mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ if (mos7840_port->read_urb->status != -EINPROGRESS) {
+ mos7840_port->read_urb->dev = serial->dev;
+
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
+ }
+ }
+ wake_up(&mos7840_port->delta_msr_wait);
+ mos7840_port->delta_msr_cond = 1;
+ dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x\n",
+ mos7840_port->shadowLCR);
+
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_set_termios
+ * this function is called by the tty driver when it wants to change
+ * the termios structure
+ *****************************************************************************/
+
+static void mos7840_set_termios(struct usb_serial_port *port,
+ struct termios *old_termios)
+{
+ int status;
+ unsigned int cflag;
+ struct usb_serial *serial;
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+ dbg("mos7840_set_termios: START\n");
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ serial = port->serial;
+
+ if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+ dbg("%s", "Invalid Serial \n");
+ return;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return;
+
+ tty = port->tty;
+
+ if (!port->tty || !port->tty->termios) {
+ dbg("%s - no tty or termios", __FUNCTION__);
+ return;
+ }
+
+ if (!mos7840_port->open) {
+ dbg("%s - port not opened", __FUNCTION__);
+ return;
+ }
+
+ dbg("%s\n", "setting termios - ");
+
+ cflag = tty->termios->c_cflag;
+
+ if (!cflag) {
+ dbg("%s %s\n", __FUNCTION__, "cflag is NULL");
+ return;
+ }
+
+ /* check that they really want us to change something */
+ if (old_termios) {
+ if ((cflag == old_termios->c_cflag) &&
+ (RELEVANT_IFLAG(tty->termios->c_iflag) ==
+ RELEVANT_IFLAG(old_termios->c_iflag))) {
+ dbg("%s\n", "Nothing to change");
+ return;
+ }
+ }
+
+ dbg("%s - clfag %08x iflag %08x", __FUNCTION__,
+ tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag));
+
+ if (old_termios) {
+ dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__,
+ old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag));
+ }
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* change the port settings to the new ones specified */
+
+ mos7840_change_port_settings(mos7840_port, old_termios);
+
+ if (!mos7840_port->read_urb) {
+ dbg("%s", "URB KILLED !!!!!\n");
+ return;
+ }
+
+ if (mos7840_port->read_urb->status != -EINPROGRESS) {
+ mos7840_port->read_urb->dev = serial->dev;
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
+ }
+ }
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ *****************************************************************************/
+
+static int mos7840_get_lsr_info(struct moschip_port *mos7840_port,
+ unsigned int *value)
+{
+ int count;
+ unsigned int result = 0;
+
+ count = mos7840_chars_in_buffer(mos7840_port->port);
+ if (count == 0) {
+ dbg("%s -- Empty", __FUNCTION__);
+ result = TIOCSER_TEMT;
+ }
+
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_bytes_avail - get number of bytes available
+ *
+ * Purpose: Let user call ioctl to get the count of number of bytes available.
+ *****************************************************************************/
+
+static int mos7840_get_bytes_avail(struct moschip_port *mos7840_port,
+ unsigned int *value)
+{
+ unsigned int result = 0;
+ struct tty_struct *tty = mos7840_port->port->tty;
+
+ if (!tty)
+ return -ENOIOCTLCMD;
+
+ result = tty->read_cnt;
+
+ dbg("%s(%d) = %d", __FUNCTION__, mos7840_port->port->number, result);
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+
+ return -ENOIOCTLCMD;
+}
+
+/*****************************************************************************
+ * mos7840_set_modem_info
+ * function to set modem info
+ *****************************************************************************/
+
+static int mos7840_set_modem_info(struct moschip_port *mos7840_port,
+ unsigned int cmd, unsigned int *value)
+{
+ unsigned int mcr;
+ unsigned int arg;
+ __u16 Data;
+ int status;
+ struct usb_serial_port *port;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ mcr = mos7840_port->shadowMCR;
+
+ if (copy_from_user(&arg, value, sizeof(int)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ if (arg & TIOCM_DTR)
+ mcr |= MCR_RTS;
+ if (arg & TIOCM_LOOP)
+ mcr |= MCR_LOOPBACK;
+ break;
+
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ mcr &= ~MCR_RTS;
+ if (arg & TIOCM_DTR)
+ mcr &= ~MCR_RTS;
+ if (arg & TIOCM_LOOP)
+ mcr &= ~MCR_LOOPBACK;
+ break;
+
+ case TIOCMSET:
+ /* turn off the RTS and DTR and LOOPBACK
+ * and then only turn on what was asked to */
+ mcr &= ~(MCR_RTS | MCR_DTR | MCR_LOOPBACK);
+ mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0);
+ mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0);
+ mcr |= ((arg & TIOCM_LOOP) ? MCR_LOOPBACK : 0);
+ break;
+ }
+
+ mos7840_port->shadowMCR = mcr;
+
+ Data = mos7840_port->shadowMCR;
+ status = 0;
+ status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_modem_info
+ * function to get modem info
+ *****************************************************************************/
+
+static int mos7840_get_modem_info(struct moschip_port *mos7840_port,
+ unsigned int *value)
+{
+ unsigned int result = 0;
+ __u16 msr;
+ unsigned int mcr = mos7840_port->shadowMCR;
+ int status = 0;
+ status =
+ mos7840_get_uart_reg(mos7840_port->port, MODEM_STATUS_REGISTER,
+ &msr);
+ result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */
+ |((mcr & MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */
+ |((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */
+ |((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0) /* 0x040 */
+ |((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0) /* 0x080 */
+ |((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */
+
+ dbg("%s -- %x", __FUNCTION__, result);
+
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_serial_info
+ * function to get information about serial port
+ *****************************************************************************/
+
+static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
+ struct serial_struct *retinfo)
+{
+ struct serial_struct tmp;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ tmp.type = PORT_16550A;
+ tmp.line = mos7840_port->port->serial->minor;
+ tmp.port = mos7840_port->port->number;
+ tmp.irq = 0;
+ tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+ tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+ tmp.baud_base = 9600;
+ tmp.close_delay = 5 * HZ;
+ tmp.closing_wait = 30 * HZ;
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+/*****************************************************************************
+ * SerialIoctl
+ * this function handles any ioctl calls to the driver
+ *****************************************************************************/
+
+static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+
+ struct async_icount cnow;
+ struct async_icount cprev;
+ struct serial_icounter_struct icount;
+ int mosret = 0;
+ int retval;
+ struct tty_ldisc *ld;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ tty = mos7840_port->port->tty;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
+
+ switch (cmd) {
+ /* return number of bytes available */
+
+ case TIOCINQ:
+ dbg("%s (%d) TIOCINQ", __FUNCTION__, port->number);
+ return mos7840_get_bytes_avail(mos7840_port,
+ (unsigned int *)arg);
+ break;
+
+ case TIOCOUTQ:
+ dbg("%s (%d) TIOCOUTQ", __FUNCTION__, port->number);
+ return put_user(tty->driver->chars_in_buffer ?
+ tty->driver->chars_in_buffer(tty) : 0,
+ (int __user *)arg);
+ break;
+
+ case TCFLSH:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+
+ ld = tty_ldisc_ref(tty);
+ switch (arg) {
+ case TCIFLUSH:
+ if (ld && ld->flush_buffer)
+ ld->flush_buffer(tty);
+ break;
+ case TCIOFLUSH:
+ if (ld && ld->flush_buffer)
+ ld->flush_buffer(tty);
+ /* fall through */
+ case TCOFLUSH:
+ if (tty->driver->flush_buffer)
+ tty->driver->flush_buffer(tty);
+ break;
+ default:
+ tty_ldisc_deref(ld);
+ return -EINVAL;
+ }
+ tty_ldisc_deref(ld);
+ return 0;
+
+ case TCGETS:
+ if (kernel_termios_to_user_termios
+ ((struct termios __user *)arg, tty->termios))
+ return -EFAULT;
+ return 0;
+
+ case TIOCSERGETLSR:
+ dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__, port->number);
+ return mos7840_get_lsr_info(mos7840_port, (unsigned int *)arg);
+ return 0;
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__,
+ port->number);
+ mosret =
+ mos7840_set_modem_info(mos7840_port, cmd,
+ (unsigned int *)arg);
+ return mosret;
+
+ case TIOCMGET:
+ dbg("%s (%d) TIOCMGET", __FUNCTION__, port->number);
+ return mos7840_get_modem_info(mos7840_port,
+ (unsigned int *)arg);
+
+ case TIOCGSERIAL:
+ dbg("%s (%d) TIOCGSERIAL", __FUNCTION__, port->number);
+ return mos7840_get_serial_info(mos7840_port,
+ (struct serial_struct *)arg);
+
+ case TIOCSSERIAL:
+ dbg("%s (%d) TIOCSSERIAL", __FUNCTION__, port->number);
+ break;
+
+ case TIOCMIWAIT:
+ dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number);
+ cprev = mos7840_port->icount;
+ while (1) {
+ //interruptible_sleep_on(&mos7840_port->delta_msr_wait);
+ mos7840_port->delta_msr_cond = 0;
+ wait_event_interruptible(mos7840_port->delta_msr_wait,
+ (mos7840_port->
+ delta_msr_cond == 1));
+
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ cnow = mos7840_port->icount;
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+ return 0;
+ }
+ cprev = cnow;
+ }
+ /* NOTREACHED */
+ break;
+
+ case TIOCGICOUNT:
+ cnow = mos7840_port->icount;
+ icount.cts = cnow.cts;
+ icount.dsr = cnow.dsr;
+ icount.rng = cnow.rng;
+ icount.dcd = cnow.dcd;
+ icount.rx = cnow.rx;
+ icount.tx = cnow.tx;
+ icount.frame = cnow.frame;
+ icount.overrun = cnow.overrun;
+ icount.parity = cnow.parity;
+ icount.brk = cnow.brk;
+ icount.buf_overrun = cnow.buf_overrun;
+
+ dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__,
+ port->number, icount.rx, icount.tx);
+ if (copy_to_user((void *)arg, &icount, sizeof(icount)))
+ return -EFAULT;
+ return 0;
+
+ case TIOCEXBAUD:
+ return 0;
+ default:
+ break;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int mos7840_calc_num_ports(struct usb_serial *serial)
+{
+
+ dbg("numberofendpoints: %d \n",
+ (int)serial->interface->cur_altsetting->desc.bNumEndpoints);
+ dbg("numberofendpoints: %d \n",
+ (int)serial->interface->altsetting->desc.bNumEndpoints);
+ if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) {
+ mos7840_num_ports = 2;
+ serial->type->num_ports = 2;
+ } else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) {
+ mos7840_num_ports = 4;
+ serial->type->num_bulk_in = 4;
+ serial->type->num_bulk_out = 4;
+ serial->type->num_ports = 4;
+ }
+
+ return mos7840_num_ports;
+}
+
+/****************************************************************************
+ * mos7840_startup
+ ****************************************************************************/
+
+static int mos7840_startup(struct usb_serial *serial)
+{
+ struct moschip_port *mos7840_port;
+ struct usb_device *dev;
+ int i, status;
+
+ __u16 Data;
+ dbg("%s \n", " mos7840_startup :entering..........");
+
+ if (!serial) {
+ dbg("%s\n", "Invalid Handler");
+ return -1;
+ }
+
+ dev = serial->dev;
+
+ dbg("%s\n", "Entering...");
+
+ /* we set up the pointers to the endpoints in the mos7840_open *
+ * function, as the structures aren't created yet. */
+
+ /* set up port private structures */
+ for (i = 0; i < serial->num_ports; ++i) {
+ mos7840_port = kmalloc(sizeof(struct moschip_port), GFP_KERNEL);
+ if (mos7840_port == NULL) {
+ err("%s - Out of memory", __FUNCTION__);
+ return -ENOMEM;
+ }
+ memset(mos7840_port, 0, sizeof(struct moschip_port));
+
+ /* Initialize all port interrupt end point to port 0 int endpoint *
+ * Our device has only one interrupt end point comman to all port */
+
+ mos7840_port->port = serial->port[i];
+ mos7840_set_port_private(serial->port[i], mos7840_port);
+
+ mos7840_port->port_num = ((serial->port[i]->number -
+ (serial->port[i]->serial->minor)) +
+ 1);
+
+ if (mos7840_port->port_num == 1) {
+ mos7840_port->SpRegOffset = 0x0;
+ mos7840_port->ControlRegOffset = 0x1;
+ mos7840_port->DcrRegOffset = 0x4;
+ } else if ((mos7840_port->port_num == 2)
+ && (mos7840_num_ports == 4)) {
+ mos7840_port->SpRegOffset = 0x8;
+ mos7840_port->ControlRegOffset = 0x9;
+ mos7840_port->DcrRegOffset = 0x16;
+ } else if ((mos7840_port->port_num == 2)
+ && (mos7840_num_ports == 2)) {
+ mos7840_port->SpRegOffset = 0xa;
+ mos7840_port->ControlRegOffset = 0xb;
+ mos7840_port->DcrRegOffset = 0x19;
+ } else if ((mos7840_port->port_num == 3)
+ && (mos7840_num_ports == 4)) {
+ mos7840_port->SpRegOffset = 0xa;
+ mos7840_port->ControlRegOffset = 0xb;
+ mos7840_port->DcrRegOffset = 0x19;
+ } else if ((mos7840_port->port_num == 4)
+ && (mos7840_num_ports == 4)) {
+ mos7840_port->SpRegOffset = 0xc;
+ mos7840_port->ControlRegOffset = 0xd;
+ mos7840_port->DcrRegOffset = 0x1c;
+ }
+ mos7840_dump_serial_port(mos7840_port);
+
+ mos7840_set_port_private(serial->port[i], mos7840_port);
+
+ //enable rx_disable bit in control register
+
+ status =
+ mos7840_get_reg_sync(serial->port[i],
+ mos7840_port->ControlRegOffset, &Data);
+ if (status < 0) {
+ dbg("Reading ControlReg failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("ControlReg Reading success val is %x, status%d\n",
+ Data, status);
+ Data |= 0x08; //setting driver done bit
+ Data |= 0x04; //sp1_bit to have cts change reflect in modem status reg
+
+ //Data |= 0x20; //rx_disable bit
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ mos7840_port->ControlRegOffset, Data);
+ if (status < 0) {
+ dbg("Writing ControlReg failed(rx_disable) status-0x%x\n", status);
+ break;
+ } else
+ dbg("ControlReg Writing success(rx_disable) status%d\n",
+ status);
+
+ //Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 and 0x24 in DCR3
+ Data = 0x01;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ (__u16) (mos7840_port->DcrRegOffset +
+ 0), Data);
+ if (status < 0) {
+ dbg("Writing DCR0 failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("DCR0 Writing success status%d\n", status);
+
+ Data = 0x05;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ (__u16) (mos7840_port->DcrRegOffset +
+ 1), Data);
+ if (status < 0) {
+ dbg("Writing DCR1 failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("DCR1 Writing success status%d\n", status);
+
+ Data = 0x24;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ (__u16) (mos7840_port->DcrRegOffset +
+ 2), Data);
+ if (status < 0) {
+ dbg("Writing DCR2 failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("DCR2 Writing success status%d\n", status);
+
+ // write values in clkstart0x0 and clkmulti 0x20
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ CLK_START_VALUE_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status);
+
+ Data = 0x20;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER,
+ Data);
+ if (status < 0) {
+ dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n",
+ status);
+ break;
+ } else
+ dbg("CLK_MULTI_REGISTER Writing success status%d\n",
+ status);
+
+ //write value 0x0 to scratchpad register
+ Data = 0x00;
+ status = 0;
+ status =
+ mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER,
+ Data);
+ if (status < 0) {
+ dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n",
+ status);
+ break;
+ } else
+ dbg("SCRATCH_PAD_REGISTER Writing success status%d\n",
+ status);
+
+ //Zero Length flag register
+ if ((mos7840_port->port_num != 1)
+ && (mos7840_num_ports == 2)) {
+
+ Data = 0xff;
+ status = 0;
+ status = mos7840_set_reg_sync(serial->port[i],
+ (__u16) (ZLP_REG1 +
+ ((__u16)
+ mos7840_port->
+ port_num)),
+ Data);
+ dbg("ZLIP offset%x\n",
+ (__u16) (ZLP_REG1 +
+ ((__u16) mos7840_port->port_num)));
+ if (status < 0) {
+ dbg("Writing ZLP_REG%d failed status-0x%x\n",
+ i + 2, status);
+ break;
+ } else
+ dbg("ZLP_REG%d Writing success status%d\n",
+ i + 2, status);
+ } else {
+ Data = 0xff;
+ status = 0;
+ status = mos7840_set_reg_sync(serial->port[i],
+ (__u16) (ZLP_REG1 +
+ ((__u16)
+ mos7840_port->
+ port_num) -
+ 0x1), Data);
+ dbg("ZLIP offset%x\n",
+ (__u16) (ZLP_REG1 +
+ ((__u16) mos7840_port->port_num) - 0x1));
+ if (status < 0) {
+ dbg("Writing ZLP_REG%d failed status-0x%x\n",
+ i + 1, status);
+ break;
+ } else
+ dbg("ZLP_REG%d Writing success status%d\n",
+ i + 1, status);
+
+ }
+ mos7840_port->control_urb = usb_alloc_urb(0, SLAB_ATOMIC);
+ mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
+
+ }
+
+ //Zero Length flag enable
+ Data = 0x0f;
+ status = 0;
+ status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
+ if (status < 0) {
+ dbg("Writing ZLP_REG5 failed status-0x%x\n", status);
+ return -1;
+ } else
+ dbg("ZLP_REG5 Writing success status%d\n", status);
+
+ /* setting configuration feature to one */
+ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ (__u8) 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 5 * HZ);
+ return 0;
+}
+
+/****************************************************************************
+ * mos7840_shutdown
+ * This function is called whenever the device is removed from the usb bus.
+ ****************************************************************************/
+
+static void mos7840_shutdown(struct usb_serial *serial)
+{
+ int i;
+ struct moschip_port *mos7840_port;
+ dbg("%s \n", " shutdown :entering..........");
+
+ if (!serial) {
+ dbg("%s", "Invalid Handler \n");
+ return;
+ }
+
+ /* check for the ports to be closed,close the ports and disconnect */
+
+ /* free private structure allocated for serial port *
+ * stop reads and writes on all ports */
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ mos7840_port = mos7840_get_port_private(serial->port[i]);
+ kfree(mos7840_port->ctrl_buf);
+ usb_kill_urb(mos7840_port->control_urb);
+ kfree(mos7840_port);
+ mos7840_set_port_private(serial->port[i], NULL);
+ }
+
+ dbg("%s\n", "Thank u :: ");
+
+}
+
+static struct usb_serial_driver moschip7840_4port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mos7840",
+ },
+ .description = DRIVER_DESC,
+ .id_table = moschip_port_id_table,
+ .num_interrupt_in = 1, //NUM_DONT_CARE,//1,
+#ifdef check
+ .num_bulk_in = 4,
+ .num_bulk_out = 4,
+ .num_ports = 4,
+#endif
+ .open = mos7840_open,
+ .close = mos7840_close,
+ .write = mos7840_write,
+ .write_room = mos7840_write_room,
+ .chars_in_buffer = mos7840_chars_in_buffer,
+ .throttle = mos7840_throttle,
+ .unthrottle = mos7840_unthrottle,
+ .calc_num_ports = mos7840_calc_num_ports,
+#ifdef MCSSerialProbe
+ .probe = mos7840_serial_probe,
+#endif
+ .ioctl = mos7840_ioctl,
+ .set_termios = mos7840_set_termios,
+ .break_ctl = mos7840_break,
+ .tiocmget = mos7840_tiocmget,
+ .tiocmset = mos7840_tiocmset,
+ .attach = mos7840_startup,
+ .shutdown = mos7840_shutdown,
+ .read_bulk_callback = mos7840_bulk_in_callback,
+ .read_int_callback = mos7840_interrupt_callback,
+};
+
+static struct usb_driver io_driver = {
+ .name = "mos7840",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = moschip_id_table_combined,
+};
+
+/****************************************************************************
+ * moschip7840_init
+ * This is called by the module subsystem, or on startup to initialize us
+ ****************************************************************************/
+static int __init moschip7840_init(void)
+{
+ int retval;
+
+ dbg("%s \n", " mos7840_init :entering..........");
+
+ /* Register with the usb serial */
+ retval = usb_serial_register(&moschip7840_4port_device);
+
+ if (retval)
+ goto failed_port_device_register;
+
+ dbg("%s\n", "Entring...");
+ info(DRIVER_DESC " " DRIVER_VERSION);
+
+ /* Register with the usb */
+ retval = usb_register(&io_driver);
+
+ if (retval)
+ goto failed_usb_register;
+
+ if (retval == 0) {
+ dbg("%s\n", "Leaving...");
+ return 0;
+ }
+
+ failed_usb_register:
+ usb_serial_deregister(&moschip7840_4port_device);
+
+ failed_port_device_register:
+
+ return retval;
+}
+
+/****************************************************************************
+ * moschip7840_exit
+ * Called when the driver is about to be unloaded.
+ ****************************************************************************/
+static void __exit moschip7840_exit(void)
+{
+
+ dbg("%s \n", " mos7840_exit :entering..........");
+
+ usb_deregister(&io_driver);
+
+ usb_serial_deregister(&moschip7840_4port_device);
+
+ dbg("%s\n", "Entring...");
+}
+
+module_init(moschip7840_init);
+module_exit(moschip7840_exit);
+
+/* Module information */
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index e49f40913c2..a764ff4e326 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -256,14 +256,14 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf
return (0);
}
- spin_lock(&wport->lock);
+ spin_lock_bh(&wport->lock);
if (wport->write_urb_busy) {
- spin_unlock(&wport->lock);
+ spin_unlock_bh(&wport->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
wport->write_urb_busy = 1;
- spin_unlock(&wport->lock);
+ spin_unlock_bh(&wport->lock);
count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 65e4d046951..9c18173e33f 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -81,10 +81,12 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
{ USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
{ USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
+ { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
+ { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) },
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, id_table);
+MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_driver pl2303_driver = {
.name = "pl2303",
@@ -127,65 +129,6 @@ static struct usb_driver pl2303_driver = {
#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80
-/* function prototypes for a PL2303 serial converter */
-static int pl2303_open (struct usb_serial_port *port, struct file *filp);
-static void pl2303_close (struct usb_serial_port *port, struct file *filp);
-static void pl2303_set_termios (struct usb_serial_port *port,
- struct termios *old);
-static int pl2303_ioctl (struct usb_serial_port *port, struct file *file,
- unsigned int cmd, unsigned long arg);
-static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs);
-static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
-static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
-static int pl2303_write (struct usb_serial_port *port,
- const unsigned char *buf, int count);
-static void pl2303_send (struct usb_serial_port *port);
-static int pl2303_write_room(struct usb_serial_port *port);
-static int pl2303_chars_in_buffer(struct usb_serial_port *port);
-static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
-static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file);
-static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
- unsigned int set, unsigned int clear);
-static int pl2303_startup (struct usb_serial *serial);
-static void pl2303_shutdown (struct usb_serial *serial);
-static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
-static void pl2303_buf_free(struct pl2303_buf *pb);
-static void pl2303_buf_clear(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
- unsigned int count);
-static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
- unsigned int count);
-
-
-/* All of the device info needed for the PL2303 SIO serial converter */
-static struct usb_serial_driver pl2303_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "pl2303",
- },
- .id_table = id_table,
- .num_interrupt_in = NUM_DONT_CARE,
- .num_bulk_in = 1,
- .num_bulk_out = 1,
- .num_ports = 1,
- .open = pl2303_open,
- .close = pl2303_close,
- .write = pl2303_write,
- .ioctl = pl2303_ioctl,
- .break_ctl = pl2303_break_ctl,
- .set_termios = pl2303_set_termios,
- .tiocmget = pl2303_tiocmget,
- .tiocmset = pl2303_tiocmset,
- .read_bulk_callback = pl2303_read_bulk_callback,
- .read_int_callback = pl2303_read_int_callback,
- .write_bulk_callback = pl2303_write_bulk_callback,
- .write_room = pl2303_write_room,
- .chars_in_buffer = pl2303_chars_in_buffer,
- .attach = pl2303_startup,
- .shutdown = pl2303_shutdown,
-};
enum pl2303_type {
type_0, /* don't know the difference between type 0 and */
@@ -204,8 +147,166 @@ struct pl2303_private {
enum pl2303_type type;
};
+/*
+ * pl2303_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
+{
+ struct pl2303_buf *pb;
+
+ if (size == 0)
+ return NULL;
+
+ pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+ if (pb == NULL)
+ return NULL;
+
+ pb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (pb->buf_buf == NULL) {
+ kfree(pb);
+ return NULL;
+ }
+
+ pb->buf_size = size;
+ pb->buf_get = pb->buf_put = pb->buf_buf;
-static int pl2303_startup (struct usb_serial *serial)
+ return pb;
+}
+
+/*
+ * pl2303_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void pl2303_buf_free(struct pl2303_buf *pb)
+{
+ if (pb) {
+ kfree(pb->buf_buf);
+ kfree(pb);
+ }
+}
+
+/*
+ * pl2303_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void pl2303_buf_clear(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ pb->buf_get = pb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+/*
+ * pl2303_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
+{
+ if (pb == NULL)
+ return 0;
+
+ return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
+{
+ if (pb == NULL)
+ return 0;
+
+ return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+ unsigned int count)
+{
+ unsigned int len;
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_space_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_put;
+ if (count > len) {
+ memcpy(pb->buf_put, buf, len);
+ memcpy(pb->buf_buf, buf+len, count - len);
+ pb->buf_put = pb->buf_buf + count - len;
+ } else {
+ memcpy(pb->buf_put, buf, count);
+ if (count < len)
+ pb->buf_put += count;
+ else /* count == len */
+ pb->buf_put = pb->buf_buf;
+ }
+
+ return count;
+}
+
+/*
+ * pl2303_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+ unsigned int count)
+{
+ unsigned int len;
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_data_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_get;
+ if (count > len) {
+ memcpy(buf, pb->buf_get, len);
+ memcpy(buf+len, pb->buf_buf, count - len);
+ pb->buf_get = pb->buf_buf + count - len;
+ } else {
+ memcpy(buf, pb->buf_get, count);
+ if (count < len)
+ pb->buf_get += count;
+ else /* count == len */
+ pb->buf_get = pb->buf_buf;
+ }
+
+ return count;
+}
+
+static int pl2303_startup(struct usb_serial *serial)
{
struct pl2303_private *priv;
enum pl2303_type type = type_0;
@@ -247,36 +348,17 @@ cleanup:
return -ENOMEM;
}
-static int set_control_lines (struct usb_device *dev, u8 value)
+static int set_control_lines(struct usb_device *dev, u8 value)
{
int retval;
- retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0),
- SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
- value, 0, NULL, 0, 100);
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
+ value, 0, NULL, 0, 100);
dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval);
return retval;
}
-static int pl2303_write (struct usb_serial_port *port, const unsigned char *buf, int count)
-{
- struct pl2303_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
-
- dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
-
- if (!count)
- return count;
-
- spin_lock_irqsave(&priv->lock, flags);
- count = pl2303_buf_put(priv->buf, buf, count);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- pl2303_send(port);
-
- return count;
-}
-
static void pl2303_send(struct usb_serial_port *port)
{
int count, result;
@@ -293,7 +375,7 @@ static void pl2303_send(struct usb_serial_port *port)
}
count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
- port->bulk_out_size);
+ port->bulk_out_size);
if (count == 0) {
spin_unlock_irqrestore(&priv->lock, flags);
@@ -304,13 +386,15 @@ static void pl2303_send(struct usb_serial_port *port)
spin_unlock_irqrestore(&priv->lock, flags);
- usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count,
+ port->write_urb->transfer_buffer);
port->write_urb->transfer_buffer_length = count;
port->write_urb->dev = port->serial->dev;
- result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
- dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
+ dev_err(&port->dev, "%s - failed submitting write urb,"
+ " error %d\n", __FUNCTION__, result);
priv->write_urb_in_use = 0;
// TODO: reschedule pl2303_send
}
@@ -318,6 +402,26 @@ static void pl2303_send(struct usb_serial_port *port)
usb_serial_port_softint(port);
}
+static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf,
+ int count)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
+
+ if (!count)
+ return count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ count = pl2303_buf_put(priv->buf, buf, count);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ pl2303_send(port);
+
+ return count;
+}
+
static int pl2303_write_room(struct usb_serial_port *port)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -350,7 +454,8 @@ static int pl2303_chars_in_buffer(struct usb_serial_port *port)
return chars;
}
-static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+static void pl2303_set_termios(struct usb_serial_port *port,
+ struct termios *old_termios)
{
struct usb_serial *serial = port->serial;
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -371,7 +476,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
spin_lock_irqsave(&priv->lock, flags);
if (!priv->termios_initialized) {
*(port->tty->termios) = tty_std_termios;
- port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ port->tty->termios->c_cflag = B9600 | CS8 | CREAD |
+ HUPCL | CLOCAL;
priv->termios_initialized = 1;
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -380,24 +486,24 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
/* check that they really want us to change something */
if (old_termios) {
if ((cflag == old_termios->c_cflag) &&
- (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
- dbg("%s - nothing to change...", __FUNCTION__);
- return;
+ (RELEVANT_IFLAG(port->tty->termios->c_iflag) ==
+ RELEVANT_IFLAG(old_termios->c_iflag))) {
+ dbg("%s - nothing to change...", __FUNCTION__);
+ return;
}
}
- buf = kzalloc (7, GFP_KERNEL);
+ buf = kzalloc(7, GFP_KERNEL);
if (!buf) {
dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
return;
}
-
- i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
- GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
- dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+ i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+ 0, 0, buf, 7, 100);
+ dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
if (cflag & CSIZE) {
switch (cflag & CSIZE) {
@@ -429,7 +535,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
case B230400: baud = 230400; break;
case B460800: baud = 460800; break;
default:
- dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n");
+ dev_err(&port->dev, "pl2303 driver does not support"
+ " the baudrate requested (fix it)\n");
break;
}
dbg("%s - baud = %d", __FUNCTION__, baud);
@@ -469,10 +576,10 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
dbg("%s - parity = none", __FUNCTION__);
}
- i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
- SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
- dbg ("0x21:0x20:0:0 %d", i);
+ i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
+ 0, 0, buf, 7, 100);
+ dbg("0x21:0x20:0:0 %d", i);
/* change control lines if we are switching to or from B0 */
spin_lock_irqsave(&priv->lock, flags);
@@ -488,13 +595,13 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
} else {
spin_unlock_irqrestore(&priv->lock, flags);
}
-
+
buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
- i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
- GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
- dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
+ i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+ 0, 0, buf, 7, 100);
+ dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
if (cflag & CRTSCTS) {
@@ -503,18 +610,82 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
index = 0x61;
else
index = 0x41;
- i = usb_control_msg(serial->dev,
+ i = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
VENDOR_WRITE_REQUEST,
VENDOR_WRITE_REQUEST_TYPE,
0x0, index, NULL, 0, 100);
- dbg ("0x40:0x1:0x0:0x%x %d", index, i);
+ dbg("0x40:0x1:0x0:0x%x %d", index, i);
+ }
+
+ kfree(buf);
+}
+
+static void pl2303_close(struct usb_serial_port *port, struct file *filp)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int c_cflag;
+ int bps;
+ long timeout;
+ wait_queue_t wait;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* wait for data to drain from the buffer */
+ spin_lock_irqsave(&priv->lock, flags);
+ timeout = PL2303_CLOSING_WAIT;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&port->tty->write_wait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (pl2303_buf_data_avail(priv->buf) == 0 ||
+ timeout == 0 || signal_pending(current) ||
+ !usb_get_intfdata(port->serial->interface)) /* disconnect */
+ break;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ timeout = schedule_timeout(timeout);
+ spin_lock_irqsave(&priv->lock, flags);
}
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&port->tty->write_wait, &wait);
+ /* clear out any remaining data in the buffer */
+ pl2303_buf_clear(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* wait for characters to drain from the device */
+ /* (this is long enough for the entire 256 byte */
+ /* pl2303 hardware buffer to drain with no flow */
+ /* control for data rates of 1200 bps or more, */
+ /* for lower rates we should really know how much */
+ /* data is in the buffer to compute a delay */
+ /* that is not unnecessarily long) */
+ bps = tty_get_baud_rate(port->tty);
+ if (bps > 1200)
+ timeout = max((HZ*2560)/bps,HZ/10);
+ else
+ timeout = 2*HZ;
+ schedule_timeout_interruptible(timeout);
- kfree (buf);
+ /* shutdown our urbs */
+ dbg("%s - shutting down urbs", __FUNCTION__);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
+
+ if (port->tty) {
+ c_cflag = port->tty->termios->c_cflag;
+ if (c_cflag & HUPCL) {
+ /* drop DTR and RTS */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_control = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ set_control_lines(port->serial->dev, 0);
+ }
+ }
}
-static int pl2303_open (struct usb_serial_port *port, struct file *filp)
+static int pl2303_open(struct usb_serial_port *port, struct file *filp)
{
struct termios tmp_termios;
struct usb_serial *serial = port->serial;
@@ -568,98 +739,35 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
/* Setup termios */
if (port->tty) {
- pl2303_set_termios (port, &tmp_termios);
+ pl2303_set_termios(port, &tmp_termios);
}
//FIXME: need to assert RTS and DTR if CRTSCTS off
dbg("%s - submitting read urb", __FUNCTION__);
port->read_urb->dev = serial->dev;
- result = usb_submit_urb (port->read_urb, GFP_KERNEL);
+ result = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (result) {
- dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
- pl2303_close (port, NULL);
+ dev_err(&port->dev, "%s - failed submitting read urb,"
+ " error %d\n", __FUNCTION__, result);
+ pl2303_close(port, NULL);
return -EPROTO;
}
dbg("%s - submitting interrupt urb", __FUNCTION__);
port->interrupt_in_urb->dev = serial->dev;
- result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL);
+ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
- dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result);
- pl2303_close (port, NULL);
+ dev_err(&port->dev, "%s - failed submitting interrupt urb,"
+ " error %d\n", __FUNCTION__, result);
+ pl2303_close(port, NULL);
return -EPROTO;
}
return 0;
}
-
-static void pl2303_close (struct usb_serial_port *port, struct file *filp)
-{
- struct pl2303_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- unsigned int c_cflag;
- int bps;
- long timeout;
- wait_queue_t wait;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- /* wait for data to drain from the buffer */
- spin_lock_irqsave(&priv->lock, flags);
- timeout = PL2303_CLOSING_WAIT;
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&port->tty->write_wait, &wait);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (pl2303_buf_data_avail(priv->buf) == 0
- || timeout == 0 || signal_pending(current)
- || !usb_get_intfdata(port->serial->interface)) /* disconnect */
- break;
- spin_unlock_irqrestore(&priv->lock, flags);
- timeout = schedule_timeout(timeout);
- spin_lock_irqsave(&priv->lock, flags);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->tty->write_wait, &wait);
- /* clear out any remaining data in the buffer */
- pl2303_buf_clear(priv->buf);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* wait for characters to drain from the device */
- /* (this is long enough for the entire 256 byte */
- /* pl2303 hardware buffer to drain with no flow */
- /* control for data rates of 1200 bps or more, */
- /* for lower rates we should really know how much */
- /* data is in the buffer to compute a delay */
- /* that is not unnecessarily long) */
- bps = tty_get_baud_rate(port->tty);
- if (bps > 1200)
- timeout = max((HZ*2560)/bps,HZ/10);
- else
- timeout = 2*HZ;
- schedule_timeout_interruptible(timeout);
-
- /* shutdown our urbs */
- dbg("%s - shutting down urbs", __FUNCTION__);
- usb_kill_urb(port->write_urb);
- usb_kill_urb(port->read_urb);
- usb_kill_urb(port->interrupt_in_urb);
-
- if (port->tty) {
- c_cflag = port->tty->termios->c_cflag;
- if (c_cflag & HUPCL) {
- /* drop DTR and RTS */
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control = 0;
- spin_unlock_irqrestore (&priv->lock, flags);
- set_control_lines (port->serial->dev, 0);
- }
- }
-}
-
-static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
- unsigned int set, unsigned int clear)
+static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file,
+ unsigned int set, unsigned int clear)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
@@ -668,7 +776,7 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
if (!usb_get_intfdata(port->serial->interface))
return -ENODEV;
- spin_lock_irqsave (&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
if (set & TIOCM_RTS)
priv->line_control |= CONTROL_RTS;
if (set & TIOCM_DTR)
@@ -678,12 +786,12 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
if (clear & TIOCM_DTR)
priv->line_control &= ~CONTROL_DTR;
control = priv->line_control;
- spin_unlock_irqrestore (&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
- return set_control_lines (port->serial->dev, control);
+ return set_control_lines(port->serial->dev, control);
}
-static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
+static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
@@ -696,10 +804,10 @@ static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
if (!usb_get_intfdata(port->serial->interface))
return -ENODEV;
- spin_lock_irqsave (&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
mcr = priv->line_control;
status = priv->line_status;
- spin_unlock_irqrestore (&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0)
| ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0)
@@ -721,22 +829,22 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
unsigned int status;
unsigned int changed;
- spin_lock_irqsave (&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
prevstatus = priv->line_status;
- spin_unlock_irqrestore (&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
while (1) {
interruptible_sleep_on(&priv->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
-
- spin_lock_irqsave (&priv->lock, flags);
+
+ spin_lock_irqsave(&priv->lock, flags);
status = priv->line_status;
- spin_unlock_irqrestore (&priv->lock, flags);
-
+ spin_unlock_irqrestore(&priv->lock, flags);
+
changed=prevstatus^status;
-
+
if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
((arg & TIOCM_CD) && (changed & UART_DCD)) ||
@@ -749,7 +857,8 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
return 0;
}
-static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
+static int pl2303_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
@@ -766,7 +875,7 @@ static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsign
return -ENOIOCTLCMD;
}
-static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
+static void pl2303_break_ctl(struct usb_serial_port *port, int break_state)
{
struct usb_serial *serial = port->serial;
u16 state;
@@ -780,15 +889,14 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
state = BREAK_ON;
dbg("%s - turning break %s", __FUNCTION__, state==BREAK_OFF ? "off" : "on");
- result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
- BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
- 0, NULL, 0, 100);
+ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
+ 0, NULL, 0, 100);
if (result)
dbg("%s - error sending break = %d", __FUNCTION__, result);
}
-
-static void pl2303_shutdown (struct usb_serial *serial)
+static void pl2303_shutdown(struct usb_serial *serial)
{
int i;
struct pl2303_private *priv;
@@ -802,7 +910,7 @@ static void pl2303_shutdown (struct usb_serial *serial)
kfree(priv);
usb_set_serial_port_data(serial->port[i], NULL);
}
- }
+ }
}
static void pl2303_update_line_status(struct usb_serial_port *port,
@@ -814,29 +922,33 @@ static void pl2303_update_line_status(struct usb_serial_port *port,
unsigned long flags;
u8 status_idx = UART_STATE;
u8 length = UART_STATE + 1;
+ u16 idv, idp;
+
+ idv = le16_to_cpu(port->serial->dev->descriptor.idVendor);
+ idp = le16_to_cpu(port->serial->dev->descriptor.idProduct);
- if ((le16_to_cpu(port->serial->dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
- (le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X65 ||
- le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_SX1 ||
- le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X75)) {
- length = 1;
- status_idx = 0;
+
+ if (idv == SIEMENS_VENDOR_ID) {
+ if (idp == SIEMENS_PRODUCT_ID_X65 ||
+ idp == SIEMENS_PRODUCT_ID_SX1 ||
+ idp == SIEMENS_PRODUCT_ID_X75) {
+
+ length = 1;
+ status_idx = 0;
+ }
}
if (actual_length < length)
- goto exit;
+ return;
/* Save off the uart status for others to look at */
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = data[status_idx];
spin_unlock_irqrestore(&priv->lock, flags);
- wake_up_interruptible (&priv->delta_msr_wait);
-
-exit:
- return;
+ wake_up_interruptible(&priv->delta_msr_wait);
}
-static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_read_int_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
unsigned char *data = urb->transfer_buffer;
@@ -853,25 +965,29 @@ static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+ urb->status);
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ urb->actual_length, urb->transfer_buffer);
+
pl2303_update_line_status(port, data, actual_length);
exit:
- status = usb_submit_urb (urb, GFP_ATOMIC);
+ status = usb_submit_urb(urb, GFP_ATOMIC);
if (status)
- dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
+ dev_err(&urb->dev->dev,
+ "%s - usb_submit_urb failed with result %d\n",
__FUNCTION__, status);
}
-
-static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -892,20 +1008,25 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
return;
}
if (urb->status == -EPROTO) {
- /* PL2303 mysteriously fails with -EPROTO reschedule the read */
- dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__);
+ /* PL2303 mysteriously fails with -EPROTO reschedule
+ * the read */
+ dbg("%s - caught -EPROTO, resubmitting the urb",
+ __FUNCTION__);
urb->status = 0;
urb->dev = port->serial->dev;
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+ dev_err(&urb->dev->dev, "%s - failed"
+ " resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
return;
}
dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
return;
}
- usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ urb->actual_length, data);
/* get tty_flag from status */
tty_flag = TTY_NORMAL;
@@ -914,7 +1035,7 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
status = priv->line_status;
priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
spin_unlock_irqrestore(&priv->lock, flags);
- wake_up_interruptible (&priv->delta_msr_wait);
+ wake_up_interruptible(&priv->delta_msr_wait);
/* break takes precedence over parity, */
/* which takes precedence over framing errors */
@@ -933,8 +1054,8 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
if (status & UART_OVERRUN_ERROR)
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
for (i = 0; i < urb->actual_length; ++i)
- tty_insert_flip_char (tty, data[i], tty_flag);
- tty_flip_buffer_push (tty);
+ tty_insert_flip_char(tty, data[i], tty_flag);
+ tty_flip_buffer_push(tty);
}
/* Schedule the next read _if_ we are still open */
@@ -942,15 +1063,14 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
urb->dev = port->serial->dev;
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+ dev_err(&urb->dev->dev, "%s - failed resubmitting"
+ " read urb, error %d\n", __FUNCTION__, result);
}
return;
}
-
-
-static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -966,18 +1086,21 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
priv->write_urb_in_use = 0;
return;
default:
/* error in the urb, so we have to resubmit it */
dbg("%s - Overflow in write", __FUNCTION__);
- dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+ dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
+ urb->status);
port->write_urb->transfer_buffer_length = 1;
port->write_urb->dev = port->serial->dev;
- result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result);
+ dev_err(&urb->dev->dev, "%s - failed resubmitting write"
+ " urb, error %d\n", __FUNCTION__, result);
else
return;
}
@@ -988,191 +1111,38 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
pl2303_send(port);
}
+/* All of the device info needed for the PL2303 SIO serial converter */
+static struct usb_serial_driver pl2303_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pl2303",
+ },
+ .id_table = id_table,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = 1,
+ .num_bulk_out = 1,
+ .num_ports = 1,
+ .open = pl2303_open,
+ .close = pl2303_close,
+ .write = pl2303_write,
+ .ioctl = pl2303_ioctl,
+ .break_ctl = pl2303_break_ctl,
+ .set_termios = pl2303_set_termios,
+ .tiocmget = pl2303_tiocmget,
+ .tiocmset = pl2303_tiocmset,
+ .read_bulk_callback = pl2303_read_bulk_callback,
+ .read_int_callback = pl2303_read_int_callback,
+ .write_bulk_callback = pl2303_write_bulk_callback,
+ .write_room = pl2303_write_room,
+ .chars_in_buffer = pl2303_chars_in_buffer,
+ .attach = pl2303_startup,
+ .shutdown = pl2303_shutdown,
+};
-/*
- * pl2303_buf_alloc
- *
- * Allocate a circular buffer and all associated memory.
- */
-
-static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
-{
-
- struct pl2303_buf *pb;
-
-
- if (size == 0)
- return NULL;
-
- pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
- if (pb == NULL)
- return NULL;
-
- pb->buf_buf = kmalloc(size, GFP_KERNEL);
- if (pb->buf_buf == NULL) {
- kfree(pb);
- return NULL;
- }
-
- pb->buf_size = size;
- pb->buf_get = pb->buf_put = pb->buf_buf;
-
- return pb;
-
-}
-
-
-/*
- * pl2303_buf_free
- *
- * Free the buffer and all associated memory.
- */
-
-static void pl2303_buf_free(struct pl2303_buf *pb)
-{
- if (pb) {
- kfree(pb->buf_buf);
- kfree(pb);
- }
-}
-
-
-/*
- * pl2303_buf_clear
- *
- * Clear out all data in the circular buffer.
- */
-
-static void pl2303_buf_clear(struct pl2303_buf *pb)
-{
- if (pb != NULL)
- pb->buf_get = pb->buf_put;
- /* equivalent to a get of all data available */
-}
-
-
-/*
- * pl2303_buf_data_avail
- *
- * Return the number of bytes of data available in the circular
- * buffer.
- */
-
-static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
-{
- if (pb != NULL)
- return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
- else
- return 0;
-}
-
-
-/*
- * pl2303_buf_space_avail
- *
- * Return the number of bytes of space available in the circular
- * buffer.
- */
-
-static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
-{
- if (pb != NULL)
- return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
- else
- return 0;
-}
-
-
-/*
- * pl2303_buf_put
- *
- * Copy data data from a user buffer and put it into the circular buffer.
- * Restrict to the amount of space available.
- *
- * Return the number of bytes copied.
- */
-
-static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
- unsigned int count)
-{
-
- unsigned int len;
-
-
- if (pb == NULL)
- return 0;
-
- len = pl2303_buf_space_avail(pb);
- if (count > len)
- count = len;
-
- if (count == 0)
- return 0;
-
- len = pb->buf_buf + pb->buf_size - pb->buf_put;
- if (count > len) {
- memcpy(pb->buf_put, buf, len);
- memcpy(pb->buf_buf, buf+len, count - len);
- pb->buf_put = pb->buf_buf + count - len;
- } else {
- memcpy(pb->buf_put, buf, count);
- if (count < len)
- pb->buf_put += count;
- else /* count == len */
- pb->buf_put = pb->buf_buf;
- }
-
- return count;
-
-}
-
-
-/*
- * pl2303_buf_get
- *
- * Get data from the circular buffer and copy to the given buffer.
- * Restrict to the amount of data available.
- *
- * Return the number of bytes copied.
- */
-
-static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
- unsigned int count)
-{
-
- unsigned int len;
-
-
- if (pb == NULL)
- return 0;
-
- len = pl2303_buf_data_avail(pb);
- if (count > len)
- count = len;
-
- if (count == 0)
- return 0;
-
- len = pb->buf_buf + pb->buf_size - pb->buf_get;
- if (count > len) {
- memcpy(buf, pb->buf_get, len);
- memcpy(buf+len, pb->buf_buf, count - len);
- pb->buf_get = pb->buf_buf + count - len;
- } else {
- memcpy(buf, pb->buf_get, count);
- if (count < len)
- pb->buf_get += count;
- else /* count == len */
- pb->buf_get = pb->buf_buf;
- }
-
- return count;
-
-}
-
-static int __init pl2303_init (void)
+static int __init pl2303_init(void)
{
int retval;
+
retval = usb_serial_register(&pl2303_device);
if (retval)
goto failed_usb_serial_register;
@@ -1187,14 +1157,12 @@ failed_usb_serial_register:
return retval;
}
-
-static void __exit pl2303_exit (void)
+static void __exit pl2303_exit(void)
{
- usb_deregister (&pl2303_driver);
- usb_serial_deregister (&pl2303_device);
+ usb_deregister(&pl2303_driver);
+ usb_serial_deregister(&pl2303_device);
}
-
module_init(pl2303_init);
module_exit(pl2303_exit);
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 55195e76eb6..65a5039665e 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -89,3 +89,11 @@
/* Belkin "F5U257" Serial Adapter */
#define BELKIN_VENDOR_ID 0x050d
#define BELKIN_PRODUCT_ID 0x0257
+
+/* Alcor Micro Corp. USB 2.0 TO RS-232 */
+#define ALCOR_VENDOR_ID 0x058F
+#define ALCOR_PRODUCT_ID 0x9720
+
+/* Huawei E620 UMTS/HSDPA card (ID: 12d1:1001) */
+#define HUAWEI_VENDOR_ID 0x12d1
+#define HUAWEI_PRODUCT_ID 0x1001
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index 789771ecdb1..1e07dfad685 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -298,14 +298,14 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i
dbg ("%s - write request of 0 bytes", __FUNCTION__);
return (0);
}
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
packet_length = port->bulk_out_size; // get max packetsize
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index e06a41bd0f3..0222d92842b 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -676,33 +676,29 @@ int usb_serial_probe(struct usb_interface *interface,
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
-
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
+
+ if (usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
dbg("found bulk in on endpoint %d", i);
bulk_in_endpoint[num_bulk_in] = endpoint;
++num_bulk_in;
}
- if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
+ if (usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dbg("found bulk out on endpoint %d", i);
bulk_out_endpoint[num_bulk_out] = endpoint;
++num_bulk_out;
}
-
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x03)) {
+
+ if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in on endpoint %d", i);
interrupt_in_endpoint[num_interrupt_in] = endpoint;
++num_interrupt_in;
}
- if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
- ((endpoint->bmAttributes & 3) == 0x03)) {
+ if (usb_endpoint_is_int_out(endpoint)) {
/* we found an interrupt out endpoint */
dbg("found interrupt out on endpoint %d", i);
interrupt_out_endpoint[num_interrupt_out] = endpoint;
@@ -716,14 +712,15 @@ int usb_serial_probe(struct usb_interface *interface,
if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
(le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
- (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) {
+ (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||
+ ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&
+ (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID))) {
if (interface != dev->actconfig->interface[0]) {
/* check out the endpoints of the other interface*/
iface_desc = dev->actconfig->interface[0]->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x03)) {
+ if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in for Prolific device on separate interface");
interrupt_in_endpoint[num_interrupt_in] = endpoint;
@@ -937,7 +934,10 @@ int usb_serial_probe(struct usb_interface *interface,
snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);
dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);
- device_register (&port->dev);
+ retval = device_register(&port->dev);
+ if (retval)
+ dev_err(&port->dev, "Error registering port device, "
+ "continuing\n");
}
usb_serial_console_init (debug, minor);
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index be9eec22574..86e48c42d6a 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -135,6 +135,18 @@ config USB_STORAGE_ONETOUCH
this input in any keybinding software. (e.g. gnome's keyboard short-
cuts)
+config USB_STORAGE_KARMA
+ bool "Support for Rio Karma music player"
+ depends on USB_STORAGE
+ help
+ Say Y here to include additional code to support the Rio Karma
+ USB interface.
+
+ This code places the Rio Karma into mass storage mode, enabling
+ it to be mounted as an ordinary filesystem. Performing an eject
+ on the resulting scsi device node returns the Karma to normal
+ operation.
+
config USB_LIBUSUAL
bool "The shared table of common (or usual) storage devices"
depends on USB
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index 8cbba22508a..023969b4385 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -20,6 +20,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o
usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o
usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
initializers.o $(usb-storage-obj-y)
diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c
index ab173b30076..5b06f9240d0 100644
--- a/drivers/usb/storage/initializers.c
+++ b/drivers/usb/storage/initializers.c
@@ -45,12 +45,6 @@
#include "debug.h"
#include "transport.h"
-#define RIO_MSC 0x08
-#define RIOP_INIT "RIOP\x00\x01\x08"
-#define RIOP_INIT_LEN 7
-#define RIO_SEND_LEN 40
-#define RIO_RECV_LEN 0x200
-
/* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
* mode */
int usb_stor_euscsi_init(struct us_data *us)
@@ -97,70 +91,3 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
return (res ? -1 : 0);
}
-
-/* Place the Rio Karma into mass storage mode.
- *
- * The initialization begins by sending 40 bytes starting
- * RIOP\x00\x01\x08\x00, which the device will ack with a 512-byte
- * packet with the high four bits set and everything else null.
- *
- * Next, we send RIOP\x80\x00\x08\x00. Each time, a 512 byte response
- * must be read, but we must loop until byte 5 in the response is 0x08,
- * indicating success. */
-int rio_karma_init(struct us_data *us)
-{
- int result, partial;
- char *recv;
- unsigned long timeout;
-
- // us->iobuf is big enough to hold cmd but not receive
- if (!(recv = kmalloc(RIO_RECV_LEN, GFP_KERNEL)))
- goto die_nomem;
-
- US_DEBUGP("Initializing Karma...\n");
-
- memset(us->iobuf, 0, RIO_SEND_LEN);
- memcpy(us->iobuf, RIOP_INIT, RIOP_INIT_LEN);
-
- result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
- us->iobuf, RIO_SEND_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
- recv, RIO_RECV_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- us->iobuf[4] = 0x80;
- us->iobuf[5] = 0;
- timeout = jiffies + msecs_to_jiffies(3000);
- for (;;) {
- US_DEBUGP("Sending init command\n");
- result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
- us->iobuf, RIO_SEND_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
- recv, RIO_RECV_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- if (recv[5] == RIO_MSC)
- break;
- if (time_after(jiffies, timeout))
- goto die;
- msleep(10);
- }
- US_DEBUGP("Karma initialized.\n");
- kfree(recv);
- return 0;
-
-die:
- kfree(recv);
-die_nomem:
- US_DEBUGP("Could not initialize karma.\n");
- return USB_STOR_TRANSPORT_FAILED;
-}
-
diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h
index 927f7781080..e2967a4d48a 100644
--- a/drivers/usb/storage/initializers.h
+++ b/drivers/usb/storage/initializers.h
@@ -47,4 +47,3 @@ int usb_stor_euscsi_init(struct us_data *us);
/* This function is required to activate all four slots on the UCR-61S2B
* flash reader */
int usb_stor_ucr61s2b_init(struct us_data *us);
-int rio_karma_init(struct us_data *us);
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
new file mode 100644
index 00000000000..0d79ae5683f
--- /dev/null
+++ b/drivers/usb/storage/karma.c
@@ -0,0 +1,155 @@
+/* Driver for Rio Karma
+ *
+ * (c) 2006 Bob Copeland <me@bobcopeland.com>
+ * (c) 2006 Keith Bennett <keith@mcs.st-and.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "debug.h"
+#include "karma.h"
+
+#define RIO_PREFIX "RIOP\x00"
+#define RIO_PREFIX_LEN 5
+#define RIO_SEND_LEN 40
+#define RIO_RECV_LEN 0x200
+
+#define RIO_ENTER_STORAGE 0x1
+#define RIO_LEAVE_STORAGE 0x2
+#define RIO_RESET 0xC
+
+extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data *);
+
+struct karma_data {
+ int in_storage;
+ char *recv;
+};
+
+/*
+ * Send commands to Rio Karma.
+ *
+ * For each command we send 40 bytes starting 'RIOP\0' followed by
+ * the command number and a sequence number, which the device will ack
+ * with a 512-byte packet with the high four bits set and everything
+ * else null. Then we send 'RIOP\x80' followed by a zero and the
+ * sequence number, until byte 5 in the response repeats the sequence
+ * number.
+ */
+static int rio_karma_send_command(char cmd, struct us_data *us)
+{
+ int result, partial;
+ unsigned long timeout;
+ static unsigned char seq = 1;
+ struct karma_data *data = (struct karma_data *) us->extra;
+
+ US_DEBUGP("karma: sending command %04x\n", cmd);
+ memset(us->iobuf, 0, RIO_SEND_LEN);
+ memcpy(us->iobuf, RIO_PREFIX, RIO_PREFIX_LEN);
+ us->iobuf[5] = cmd;
+ us->iobuf[6] = seq;
+
+ timeout = jiffies + msecs_to_jiffies(6000);
+ for (;;) {
+ result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ us->iobuf, RIO_SEND_LEN, &partial);
+ if (result != USB_STOR_XFER_GOOD)
+ goto err;
+
+ result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ data->recv, RIO_RECV_LEN, &partial);
+ if (result != USB_STOR_XFER_GOOD)
+ goto err;
+
+ if (data->recv[5] == seq)
+ break;
+
+ if (time_after(jiffies, timeout))
+ goto err;
+
+ us->iobuf[4] = 0x80;
+ us->iobuf[5] = 0;
+ msleep(50);
+ }
+
+ seq++;
+ if (seq == 0)
+ seq = 1;
+
+ US_DEBUGP("karma: sent command %04x\n", cmd);
+ return 0;
+err:
+ US_DEBUGP("karma: command %04x failed\n", cmd);
+ return USB_STOR_TRANSPORT_FAILED;
+}
+
+/*
+ * Trap START_STOP and READ_10 to leave/re-enter storage mode.
+ * Everything else is propagated to the normal bulk layer.
+ */
+int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ int ret;
+ struct karma_data *data = (struct karma_data *) us->extra;
+
+ if (srb->cmnd[0] == READ_10 && !data->in_storage) {
+ ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
+ if (ret)
+ return ret;
+
+ data->in_storage = 1;
+ return usb_stor_Bulk_transport(srb, us);
+ } else if (srb->cmnd[0] == START_STOP) {
+ ret = rio_karma_send_command(RIO_LEAVE_STORAGE, us);
+ if (ret)
+ return ret;
+
+ data->in_storage = 0;
+ return rio_karma_send_command(RIO_RESET, us);
+ }
+ return usb_stor_Bulk_transport(srb, us);
+}
+
+static void rio_karma_destructor(void *extra)
+{
+ struct karma_data *data = (struct karma_data *) extra;
+ kfree(data->recv);
+}
+
+int rio_karma_init(struct us_data *us)
+{
+ int ret = 0;
+ struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO);
+ if (!data)
+ goto out;
+
+ data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO);
+ if (!data->recv) {
+ kfree(data);
+ goto out;
+ }
+
+ us->extra = data;
+ us->extra_destructor = rio_karma_destructor;
+ ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
+ data->in_storage = (ret == 0);
+out:
+ return ret;
+}
diff --git a/drivers/usb/storage/karma.h b/drivers/usb/storage/karma.h
new file mode 100644
index 00000000000..8a60972af8c
--- /dev/null
+++ b/drivers/usb/storage/karma.h
@@ -0,0 +1,7 @@
+#ifndef _KARMA_USB_H
+#define _KARMA_USB_H
+
+extern int rio_karma_init(struct us_data *us);
+extern int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+#endif
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
index b1ec4a71854..599ad10a761 100644
--- a/drivers/usb/storage/libusual.c
+++ b/drivers/usb/storage/libusual.c
@@ -8,6 +8,7 @@
#include <linux/usb.h>
#include <linux/usb_usual.h>
#include <linux/vmalloc.h>
+#include <linux/kthread.h>
/*
*/
@@ -117,7 +118,7 @@ static int usu_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
unsigned long type;
- int rc;
+ struct task_struct* task;
unsigned long flags;
type = USB_US_TYPE(id->driver_info);
@@ -132,8 +133,9 @@ static int usu_probe(struct usb_interface *intf,
stat[type].fls |= USU_MOD_FL_THREAD;
spin_unlock_irqrestore(&usu_lock, flags);
- rc = kernel_thread(usu_probe_thread, (void*)type, CLONE_VM);
- if (rc < 0) {
+ task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type);
+ if (IS_ERR(task)) {
+ int rc = PTR_ERR(task);
printk(KERN_WARNING "libusual: "
"Unable to start the thread for %s: %d\n",
bias_names[type], rc);
@@ -175,8 +177,6 @@ static int usu_probe_thread(void *arg)
int rc;
unsigned long flags;
- daemonize("libusual_%d", type); /* "usb-storage" is kinda too long */
-
/* A completion does not work here because it's counted. */
down(&usu_init_notify);
up(&usu_init_notify);
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
index 313920d980c..f843a0bcf10 100644
--- a/drivers/usb/storage/onetouch.c
+++ b/drivers/usb/storage/onetouch.c
@@ -135,6 +135,7 @@ int onetouch_connect_input(struct us_data *ss)
struct usb_onetouch *onetouch;
struct input_dev *input_dev;
int pipe, maxp;
+ int error = -ENOMEM;
interface = ss->pusb_intf->cur_altsetting;
@@ -211,15 +212,18 @@ int onetouch_connect_input(struct us_data *ss)
ss->suspend_resume_hook = usb_onetouch_pm_hook;
#endif
- input_register_device(onetouch->dev);
+ error = input_register_device(onetouch->dev);
+ if (error)
+ goto fail3;
return 0;
+ fail3: usb_free_urb(onetouch->irq);
fail2: usb_buffer_free(udev, ONETOUCH_PKT_LEN,
onetouch->data, onetouch->data_dma);
fail1: kfree(onetouch);
input_free_device(input_dev);
- return -ENOMEM;
+ return error;
}
void onetouch_release_input(void *onetouch_)
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index a4b7df9ff8c..e1072d52d64 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -72,12 +72,27 @@ static const char* host_info(struct Scsi_Host *host)
static int slave_alloc (struct scsi_device *sdev)
{
+ struct us_data *us = host_to_us(sdev->host);
+
/*
* Set the INQUIRY transfer length to 36. We don't use any of
* the extra data and many devices choke if asked for more or
* less than 36 bytes.
*/
sdev->inquiry_len = 36;
+
+ /*
+ * The UFI spec treates the Peripheral Qualifier bits in an
+ * INQUIRY result as reserved and requires devices to set them
+ * to 0. However the SCSI spec requires these bits to be set
+ * to 3 to indicate when a LUN is not present.
+ *
+ * Let the scanning code know if this target merely sets
+ * Peripheral Device Type to 0x1f to indicate no LUN.
+ */
+ if (us->subclass == US_SC_UFI)
+ sdev->sdev_target->pdt_1f_for_no_lun = 1;
+
return 0;
}
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index d6acc92a4ae..f23514c4e64 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -294,11 +294,6 @@ static int interpret_urb_result(struct us_data *us, unsigned int pipe,
return USB_STOR_XFER_ERROR;
return USB_STOR_XFER_STALLED;
- /* timeout or excessively long NAK */
- case -ETIMEDOUT:
- US_DEBUGP("-- timeout or NAK\n");
- return USB_STOR_XFER_ERROR;
-
/* babble - the device tried to send more than we wanted to read */
case -EOVERFLOW:
US_DEBUGP("-- babble\n");
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index b130e170b4a..c9a8d50106d 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -152,6 +152,13 @@ UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ),
+/* Reported by Jon Hart <Jon.Hart@web.de> */
+UNUSUAL_DEV( 0x0421, 0x0434, 0x0100, 0x0100,
+ "Nokia",
+ "E60",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ),
+
/* Reported by Sumedha Swamy <sumedhaswamy@gmail.com> and
* Einar Th. Einarsson <einarthered@gmail.com> */
UNUSUAL_DEV( 0x0421, 0x0444, 0x0100, 0x0100,
@@ -218,10 +225,12 @@ UNUSUAL_DEV( 0x0457, 0x0151, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NOT_LOCKABLE ),
+#ifdef CONFIG_USB_STORAGE_KARMA
UNUSUAL_DEV( 0x045a, 0x5210, 0x0101, 0x0101,
"Rio",
"Rio Karma",
- US_SC_SCSI, US_PR_BULK, rio_karma_init, 0),
+ US_SC_SCSI, US_PR_KARMA, rio_karma_init, 0),
+#endif
/* Patch submitted by Philipp Friedrich <philipp@void.at> */
UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100,
@@ -631,6 +640,13 @@ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210,
"Digital Camera EX-20 DSC",
US_SC_8070, US_PR_DEVICE, NULL, 0 ),
+/* Reported by <Hendryk.Pfeiffer@gmx.de> */
+UNUSUAL_DEV( 0x059f, 0x0643, 0x0000, 0x0000,
+ "LaCie",
+ "DVD+-RW",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_GO_SLOW ),
+
/* Submitted by Joel Bourquard <numlock@freesurf.ch>
* Some versions of this device need the SubClass and Protocol overrides
* while others don't.
@@ -1254,6 +1270,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT ),
+/* Reported by Jan Mate <mate@fiit.stuba.sk> */
+UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
+ "Sony Ericsson",
+ "P990i",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
/* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */
UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000,
"Sony Ericsson",
@@ -1261,6 +1284,13 @@ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY ),
+/* Reported by Jan Mate <mate@fiit.stuba.sk> */
+UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
+ "Sony Ericsson",
+ "P990i",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
/* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
* Tested on hardware version 1.10.
* Entry is needed only for the initializer function override.
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 8d7bdcb5924..b8d6031b097 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -98,6 +98,9 @@
#ifdef CONFIG_USB_STORAGE_ALAUDA
#include "alauda.h"
#endif
+#ifdef CONFIG_USB_STORAGE_KARMA
+#include "karma.h"
+#endif
/* Some informational data */
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
@@ -646,6 +649,14 @@ static int get_transport(struct us_data *us)
break;
#endif
+#ifdef CONFIG_USB_STORAGE_KARMA
+ case US_PR_KARMA:
+ us->transport_name = "Rio Karma/Bulk";
+ us->transport = rio_karma_transport;
+ us->transport_reset = usb_stor_Bulk_reset;
+ break;
+#endif
+
default:
return -EIO;
}
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index b362039792b..1b51d3187a9 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -1,5 +1,5 @@
/*
- * USB Skeleton driver - 2.0
+ * USB Skeleton driver - 2.2
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
*
@@ -7,9 +7,8 @@
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
- * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
- * but has been rewritten to be easy to read and use, as no locks are now
- * needed anymore.
+ * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
+ * but has been rewritten to be easier to read and use.
*
*/
@@ -21,6 +20,7 @@
#include <linux/kref.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
/* Define these values to match your devices */
@@ -32,38 +32,39 @@ static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, skel_table);
+MODULE_DEVICE_TABLE(usb, skel_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192
/* our private defines. if this grows any larger, use your own .h file */
-#define MAX_TRANSFER ( PAGE_SIZE - 512 )
+#define MAX_TRANSFER (PAGE_SIZE - 512)
#define WRITES_IN_FLIGHT 8
/* Structure to hold all of our device specific stuff */
struct usb_skel {
- struct usb_device * udev; /* the usb device for this device */
- struct usb_interface * interface; /* the interface for this device */
+ struct usb_device *dev; /* the usb device for this device */
+ struct usb_interface *interface; /* the interface for this device */
struct semaphore limit_sem; /* limiting the number of writes in progress */
- unsigned char * bulk_in_buffer; /* the buffer to receive data */
+ unsigned char *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct kref kref;
+ struct mutex io_mutex; /* synchronize I/O with disconnect */
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
static struct usb_driver skel_driver;
static void skel_delete(struct kref *kref)
-{
+{
struct usb_skel *dev = to_skel_dev(kref);
usb_put_dev(dev->udev);
- kfree (dev->bulk_in_buffer);
- kfree (dev);
+ kfree(dev->bulk_in_buffer);
+ kfree(dev);
}
static int skel_open(struct inode *inode, struct file *file)
@@ -89,6 +90,11 @@ static int skel_open(struct inode *inode, struct file *file)
goto exit;
}
+ /* prevent the device from being autosuspended */
+ retval = usb_autopm_get_interface(interface);
+ if (retval)
+ goto exit;
+
/* increment our usage count for the device */
kref_get(&dev->kref);
@@ -107,6 +113,12 @@ static int skel_release(struct inode *inode, struct file *file)
if (dev == NULL)
return -ENODEV;
+ /* allow the device to be autosuspended */
+ mutex_lock(&dev->io_mutex);
+ if (dev->interface)
+ usb_autopm_put_interface(dev->interface);
+ mutex_unlock(&dev->io_mutex);
+
/* decrement the count on our device */
kref_put(&dev->kref, skel_delete);
return 0;
@@ -115,11 +127,17 @@ static int skel_release(struct inode *inode, struct file *file)
static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
- int retval = 0;
+ int retval;
int bytes_read;
dev = (struct usb_skel *)file->private_data;
-
+
+ mutex_lock(&dev->io_mutex);
+ if (!dev->interface) { /* disconnect() was called */
+ retval = -ENODEV;
+ goto exit;
+ }
+
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
@@ -135,6 +153,8 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *
retval = bytes_read;
}
+exit:
+ mutex_unlock(&dev->io_mutex);
return retval;
}
@@ -145,16 +165,16 @@ static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
dev = (struct usb_skel *)urb->context;
/* sync/async unlink faults aren't errors */
- if (urb->status &&
- !(urb->status == -ENOENT ||
+ if (urb->status &&
+ !(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)) {
- dbg("%s - nonzero write bulk status received: %d",
+ err("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
}
/* free up our allocated buffer */
- usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
@@ -179,6 +199,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
goto exit;
}
+ mutex_lock(&dev->io_mutex);
+ if (!dev->interface) { /* disconnect() was called */
+ retval = -ENODEV;
+ goto error;
+ }
+
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
@@ -213,17 +239,22 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
/* release our reference to this urb, the USB core will eventually free it entirely */
usb_free_urb(urb);
-exit:
+ mutex_unlock(&dev->io_mutex);
return writesize;
error:
- usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
- usb_free_urb(urb);
+ if (urb) {
+ usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+ mutex_unlock(&dev->io_mutex);
up(&dev->limit_sem);
+
+exit:
return retval;
}
-static struct file_operations skel_fops = {
+static const struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
@@ -231,7 +262,7 @@ static struct file_operations skel_fops = {
.release = skel_release,
};
-/*
+/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with the driver core
*/
@@ -243,7 +274,7 @@ static struct usb_class_driver skel_class = {
static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
- struct usb_skel *dev = NULL;
+ struct usb_skel *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
@@ -252,12 +283,13 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
+ if (!dev) {
err("Out of memory");
goto error;
}
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
+ mutex_init(&dev->io_mutex);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
@@ -269,10 +301,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
- ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
dev->bulk_in_size = buffer_size;
@@ -285,10 +314,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
}
if (!dev->bulk_out_endpointAddr &&
- ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_OUT) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
@@ -334,6 +360,11 @@ static void skel_disconnect(struct usb_interface *interface)
/* give back our minor */
usb_deregister_dev(interface, &skel_class);
+ /* prevent more I/O from starting */
+ mutex_lock(&dev->io_mutex);
+ dev->interface = NULL;
+ mutex_unlock(&dev->io_mutex);
+
unlock_kernel();
/* decrement our usage count */
@@ -367,7 +398,7 @@ static void __exit usb_skel_exit(void)
usb_deregister(&skel_driver);
}
-module_init (usb_skel_init);
-module_exit (usb_skel_exit);
+module_init(usb_skel_init);
+module_exit(usb_skel_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 702eb933cf8..e0ef3328942 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -825,30 +825,48 @@ config FB_I810_I2C
help
config FB_INTEL
- tristate "Intel 830M/845G/852GM/855GM/865G support (EXPERIMENTAL)"
+ tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)"
depends on FB && EXPERIMENTAL && PCI && X86
select AGP
select AGP_INTEL
+ select I2C_ALGOBIT if FB_INTEL_I2C
+ select I2C if FB_INTEL_I2C
select FB_MODE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
help
This driver supports the on-board graphics built in to the Intel
- 830M/845G/852GM/855GM/865G chipsets.
+ 830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM chipsets.
Say Y if you have and plan to use such a board.
- To compile this driver as a module, choose M here: the
+ If you say Y here and want DDC/I2C support you must first say Y to
+ "I2C support" and "I2C bit-banging support" in the character devices
+ section.
+
+ If you say M here then "I2C support" and "I2C bit-banging support"
+ can be build either as modules or built-in.
+
+ To compile this driver as a module, choose M here: the
module will be called intelfb.
+ For more information, please read <file:Documentation/fb/intelfb.txt>
+
config FB_INTEL_DEBUG
- bool "Intel driver Debug Messages"
+ bool "Intel driver Debug Messages"
depends on FB_INTEL
---help---
Say Y here if you want the Intel driver to output all sorts
of debugging informations to provide to the maintainer when
something goes wrong.
+config FB_INTEL_I2C
+ bool "DDC/I2C for Intel framebuffer support"
+ depends on FB_INTEL
+ default y
+ help
+ Say Y here if you want DDC/I2C support for your on-board Intel graphics.
+
config FB_MATROX
tristate "Matrox acceleration"
depends on FB && PCI
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c
index e308ed2d249..365de5dcc88 100644
--- a/drivers/video/aty/radeon_pm.c
+++ b/drivers/video/aty/radeon_pm.c
@@ -2621,25 +2621,28 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo)
}
-int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(pdev);
struct radeonfb_info *rinfo = info->par;
int i;
- if (state.event == pdev->dev.power.power_state.event)
+ if (mesg.event == pdev->dev.power.power_state.event)
return 0;
- printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n",
- pci_name(pdev), state.event);
+ printk(KERN_DEBUG "radeonfb (%s): suspending for event: %d...\n",
+ pci_name(pdev), mesg.event);
/* For suspend-to-disk, we cheat here. We don't suspend anything and
* let fbcon continue drawing until we are all set. That shouldn't
* really cause any problem at this point, provided that the wakeup
* code knows that any state in memory may not match the HW
*/
- if (state.event == PM_EVENT_FREEZE)
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE: /* about to take snapshot */
+ case PM_EVENT_PRETHAW: /* before restoring snapshot */
goto done;
+ }
acquire_console_sem();
@@ -2706,7 +2709,7 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
release_console_sem();
done:
- pdev->dev.power.power_state = state;
+ pdev->dev.power.power_state = mesg;
return 0;
}
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c
index ffc72ae3ada..fe1488374f6 100644
--- a/drivers/video/backlight/hp680_bl.c
+++ b/drivers/video/backlight/hp680_bl.c
@@ -20,7 +20,7 @@
#include <asm/cpu/dac.h>
#include <asm/hp6xx/hp6xx.h>
-#include <asm/hd64461/hd64461.h>
+#include <asm/hd64461.h>
#define HP680_MAX_INTENSITY 255
#define HP680_DEFAULT_INTENSITY 10
@@ -163,6 +163,6 @@ static void __exit hp680bl_exit(void)
module_init(hp680bl_init);
module_exit(hp680bl_exit);
-MODULE_AUTHOR("Andriy Skulysh <askulysh@image.kiev.ua>");
+MODULE_AUTHOR("Andriy Skulysh <askulysh@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 680 Backlight Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c
index caf1eca199b..628571c63ba 100644
--- a/drivers/video/backlight/locomolcd.c
+++ b/drivers/video/backlight/locomolcd.c
@@ -33,19 +33,19 @@ static unsigned long locomolcd_flags;
static void locomolcd_on(int comadj)
{
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 1);
mdelay(2);
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 1);
mdelay(2);
locomo_m62332_senddata(locomolcd_dev, comadj, 0);
mdelay(5);
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 1);
mdelay(10);
/* TFTCRST | CPSOUT=0 | CPSEN */
@@ -58,8 +58,8 @@ static void locomolcd_on(int comadj)
locomo_writel((0x04 | 0x01), locomolcd_dev->mapbase + LOCOMO_TC);
mdelay(10);
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 1);
}
static void locomolcd_off(int comadj)
@@ -68,16 +68,16 @@ static void locomolcd_off(int comadj)
locomo_writel(0x06, locomolcd_dev->mapbase + LOCOMO_TC);
mdelay(1);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
mdelay(110);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
mdelay(700);
/* TFTCRST=0 | CPSOUT=0 | CPSEN = 0 */
locomo_writel(0, locomolcd_dev->mapbase + LOCOMO_TC);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
}
void locomolcd_power(int on)
@@ -167,14 +167,14 @@ static int locomolcd_resume(struct locomo_dev *dev)
#define locomolcd_resume NULL
#endif
-static int locomolcd_probe(struct locomo_dev *dev)
+static int locomolcd_probe(struct locomo_dev *ldev)
{
unsigned long flags;
local_irq_save(flags);
- locomolcd_dev = dev;
+ locomolcd_dev = ldev;
- locomo_gpio_set_dir(dev, LOCOMO_GPIO_FL_VR, 0);
+ locomo_gpio_set_dir(ldev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
/* the poodle_lcd_power function is called for the first time
* from fs_initcall, which is before locomo is activated.
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 4444bef68fb..aa3935df852 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -6,7 +6,7 @@ menu "Console display driver support"
config VGA_CONSOLE
bool "VGA text console" if EMBEDDED || !X86
- depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !ARCH_VERSATILE
+ depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !ARCH_VERSATILE && !SUPERH
default y
help
Saying Y here will allow you to use Linux in text mode through a
diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c
index 4cc6b454265..3afb472763c 100644
--- a/drivers/video/hitfb.c
+++ b/drivers/video/hitfb.c
@@ -4,7 +4,7 @@
* (C) 1999 Mihai Spatar
* (C) 2000 YAEGASHI Takeshi
* (C) 2003, 2004 Paul Mundt
- * (C) 2003, 2004 Andriy Skulysh
+ * (C) 2003, 2004, 2006 Andriy Skulysh
*
* 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
@@ -20,18 +20,16 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/platform_device.h>
#include <linux/fb.h>
#include <asm/machvec.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
-#include <asm/hd64461/hd64461.h>
-
-#ifdef MACH_HP600
+#include <asm/hd64461.h>
#include <asm/cpu/dac.h>
#include <asm/hp6xx/hp6xx.h>
-#endif
#define WIDTH 640
@@ -45,7 +43,6 @@ static struct fb_var_screeninfo hitfb_var __initdata = {
static struct fb_fix_screeninfo hitfb_fix __initdata = {
.id = "Hitachi HD64461",
.type = FB_TYPE_PACKED_PIXELS,
- .ypanstep = 8,
.accel = FB_ACCEL_NONE,
};
@@ -73,26 +70,14 @@ static inline void hitfb_accel_set_dest(int truecolor, u16 dx, u16 dy,
if (truecolor)
saddr <<= 1;
- fb_writew(width, HD64461_BBTDWR);
- fb_writew(height, HD64461_BBTDHR);
+ fb_writew(width-1, HD64461_BBTDWR);
+ fb_writew(height-1, HD64461_BBTDHR);
fb_writew(saddr & 0xffff, HD64461_BBTDSARL);
fb_writew(saddr >> 16, HD64461_BBTDSARH);
}
-static inline void hitfb_accel_solidfill(int truecolor, u16 dx, u16 dy,
- u16 width, u16 height, u16 color)
-{
- hitfb_accel_set_dest(truecolor, dx, dy, width, height);
-
- fb_writew(0x00f0, HD64461_BBTROPR);
- fb_writew(16, HD64461_BBTMDR);
- fb_writew(color, HD64461_GRSCR);
-
- hitfb_accel_start(truecolor);
-}
-
static inline void hitfb_accel_bitblt(int truecolor, u16 sx, u16 sy, u16 dx,
u16 dy, u16 width, u16 height, u16 rop,
u32 mask_addr)
@@ -100,6 +85,8 @@ static inline void hitfb_accel_bitblt(int truecolor, u16 sx, u16 sy, u16 dx,
u32 saddr, daddr;
u32 maddr = 0;
+ height--;
+ width--;
fb_writew(rop, HD64461_BBTROPR);
if ((sy < dy) || ((sy == dy) && (sx <= dx))) {
saddr = WIDTH * (sy + height) + sx + width;
@@ -146,6 +133,7 @@ static void hitfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
if (rect->rop != ROP_COPY)
cfb_fillrect(p, rect);
else {
+ hitfb_accel_wait();
fb_writew(0x00f0, HD64461_BBTROPR);
fb_writew(16, HD64461_BBTMDR);
@@ -161,16 +149,15 @@ static void hitfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
rect->height);
hitfb_accel_start(0);
}
- hitfb_accel_wait();
}
}
static void hitfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
{
+ hitfb_accel_wait();
hitfb_accel_bitblt(p->var.bits_per_pixel == 16, area->sx, area->sy,
area->dx, area->dy, area->width, area->height,
0x00cc, 0);
- hitfb_accel_wait();
}
static int hitfb_pan_display(struct fb_var_screeninfo *var,
@@ -182,7 +169,7 @@ static int hitfb_pan_display(struct fb_var_screeninfo *var,
if (xoffset != 0)
return -EINVAL;
- fb_writew(yoffset, HD64461_LCDCBAR);
+ fb_writew((yoffset*info->fix.line_length)>>10, HD64461_LCDCBAR);
return 0;
}
@@ -192,12 +179,6 @@ int hitfb_blank(int blank_mode, struct fb_info *info)
unsigned short v;
if (blank_mode) {
-#ifdef MACH_HP600
- sh_dac_disable(DAC_LCD_BRIGHTNESS);
- v = fb_readw(HD64461_GPBDR);
- v |= HD64461_GPBDR_LCDOFF;
- fb_writew(v, HD64461_GPBDR);
-#endif
v = fb_readw(HD64461_LDR1);
v &= ~HD64461_LDR1_DON;
fb_writew(v, HD64461_LDR1);
@@ -213,19 +194,18 @@ int hitfb_blank(int blank_mode, struct fb_info *info)
v = fb_readw(HD64461_STBCR);
v &= ~HD64461_STBCR_SLCDST;
fb_writew(v, HD64461_STBCR);
-#ifdef MACH_HP600
- sh_dac_enable(DAC_LCD_BRIGHTNESS);
- v = fb_readw(HD64461_GPBDR);
- v &= ~HD64461_GPBDR_LCDOFF;
- fb_writew(v, HD64461_GPBDR);
-#endif
- v = fb_readw(HD64461_LDR1);
- v |= HD64461_LDR1_DON;
- fb_writew(v, HD64461_LDR1);
v = fb_readw(HD64461_LCDCCR);
- v &= ~HD64461_LCDCCR_MOFF;
+ v &= ~(HD64461_LCDCCR_MOFF | HD64461_LCDCCR_STREQ);
fb_writew(v, HD64461_LCDCCR);
+
+ do {
+ v = fb_readw(HD64461_LCDCCR);
+ } while(v&HD64461_LCDCCR_STBACK);
+
+ v = fb_readw(HD64461_LDR1);
+ v |= HD64461_LDR1_DON;
+ fb_writew(v, HD64461_LDR1);
}
return 0;
}
@@ -233,7 +213,7 @@ int hitfb_blank(int blank_mode, struct fb_info *info)
static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info)
{
- if (regno >= info->cmap.len)
+ if (regno >= 256)
return 1;
switch (info->var.bits_per_pixel) {
@@ -244,6 +224,8 @@ static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
fb_writew(blue >> 10, HD64461_CPTWDR);
break;
case 16:
+ if (regno >= 16)
+ return 1;
((u32 *) (info->pseudo_palette))[regno] =
((red & 0xf800)) |
((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
@@ -252,26 +234,113 @@ static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 0;
}
+static int hitfb_sync(struct fb_info *info)
+{
+ hitfb_accel_wait();
+
+ return 0;
+}
+
+static int hitfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ int maxy;
+
+ var->xres = info->var.xres;
+ var->xres_virtual = info->var.xres;
+ var->yres = info->var.yres;
+
+ if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16))
+ var->bits_per_pixel = info->var.bits_per_pixel;
+
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ maxy = info->fix.smem_len / var->xres;
+
+ if (var->bits_per_pixel == 16)
+ maxy /= 2;
+
+ if (var->yres_virtual > maxy)
+ var->yres_virtual = maxy;
+
+ var->xoffset = 0;
+ var->yoffset = 0;
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16: /* RGB 565 */
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int hitfb_set_par(struct fb_info *info)
+{
+ unsigned short ldr3;
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ info->fix.line_length = info->var.xres;
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ info->fix.ypanstep = 16;
+ break;
+ case 16:
+ info->fix.line_length = info->var.xres*2;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.ypanstep = 8;
+ break;
+ }
+
+ fb_writew(info->fix.line_length, HD64461_LCDCLOR);
+ ldr3 = fb_readw(HD64461_LDR3);
+ ldr3 &= ~15;
+ ldr3 |= (info->var.bits_per_pixel == 8) ? 4 : 8;
+ fb_writew(ldr3, HD64461_LDR3);
+ return 0;
+}
+
static struct fb_ops hitfb_ops = {
.owner = THIS_MODULE,
+ .fb_check_var = hitfb_check_var,
+ .fb_set_par = hitfb_set_par,
.fb_setcolreg = hitfb_setcolreg,
.fb_blank = hitfb_blank,
+ .fb_sync = hitfb_sync,
.fb_pan_display = hitfb_pan_display,
.fb_fillrect = hitfb_fillrect,
.fb_copyarea = hitfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
-int __init hitfb_init(void)
+static int __init hitfb_probe(struct platform_device *dev)
{
unsigned short lcdclor, ldr3, ldvndr;
- int size;
if (fb_get_options("hitfb", NULL))
return -ENODEV;
+ hitfb_fix.mmio_start = CONFIG_HD64461_IOBASE+0x1000;
+ hitfb_fix.mmio_len = 0x1000;
hitfb_fix.smem_start = CONFIG_HD64461_IOBASE + 0x02000000;
- hitfb_fix.smem_len = (MACH_HP690) ? 1024 * 1024 : 512 * 1024;
+ hitfb_fix.smem_len = 512 * 1024;
lcdclor = fb_readw(HD64461_LCDCLOR);
ldvndr = fb_readw(HD64461_LDVNDR);
@@ -321,12 +390,12 @@ int __init hitfb_init(void)
fb_info.var = hitfb_var;
fb_info.fix = hitfb_fix;
fb_info.pseudo_palette = pseudo_palette;
- fb_info.flags = FBINFO_DEFAULT;
+ fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN |
+ FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA;
fb_info.screen_base = (void *)hitfb_fix.smem_start;
- size = (fb_info.var.bits_per_pixel == 8) ? 256 : 16;
- fb_alloc_cmap(&fb_info.cmap, size, 0);
+ fb_alloc_cmap(&fb_info.cmap, 256, 0);
if (register_framebuffer(&fb_info) < 0)
return -EINVAL;
@@ -336,9 +405,75 @@ int __init hitfb_init(void)
return 0;
}
+static int __devexit hitfb_remove(struct platform_device *dev)
+{
+ return unregister_framebuffer(&fb_info);
+}
+
+#ifdef CONFIG_PM
+static int hitfb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ u16 v;
+
+ hitfb_blank(1,0);
+ v = fb_readw(HD64461_STBCR);
+ v |= HD64461_STBCR_SLCKE_IST;
+ fb_writew(v, HD64461_STBCR);
+
+ return 0;
+}
+
+static int hitfb_resume(struct platform_device *dev)
+{
+ u16 v;
+
+ v = fb_readw(HD64461_STBCR);
+ v &= ~HD64461_STBCR_SLCKE_OST;
+ msleep(100);
+ v = fb_readw(HD64461_STBCR);
+ v &= ~HD64461_STBCR_SLCKE_IST;
+ fb_writew(v, HD64461_STBCR);
+ hitfb_blank(0,0);
+
+ return 0;
+}
+#endif
+
+static struct platform_driver hitfb_driver = {
+ .probe = hitfb_probe,
+ .remove = __devexit_p(hitfb_remove),
+#ifdef CONFIG_PM
+ .suspend = hitfb_suspend,
+ .resume = hitfb_resume,
+#endif
+ .driver = {
+ .name = "hitfb",
+ },
+};
+
+static struct platform_device hitfb_device = {
+ .name = "hitfb",
+ .id = -1,
+};
+
+static int __init hitfb_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hitfb_driver);
+ if (!ret) {
+ ret = platform_device_register(&hitfb_device);
+ if (ret)
+ platform_driver_unregister(&hitfb_driver);
+ }
+ return ret;
+}
+
+
static void __exit hitfb_exit(void)
{
- unregister_framebuffer(&fb_info);
+ platform_device_unregister(&hitfb_device);
+ platform_driver_unregister(&hitfb_driver);
}
module_init(hitfb_init);
diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c
index c1f7b49975d..7d06b38e80a 100644
--- a/drivers/video/i810/i810-i2c.c
+++ b/drivers/video/i810/i810-i2c.c
@@ -98,7 +98,6 @@ static int i810_setup_i2c_bus(struct i810fb_i2c_chan *chan, const char *name)
chan->algo.getsda = i810i2c_getsda;
chan->algo.getscl = i810i2c_getscl;
chan->algo.udelay = 10;
- chan->algo.mdelay = 10;
chan->algo.timeout = (HZ/2);
chan->algo.data = chan;
diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c
index a6ca02f2156..d42edaccb84 100644
--- a/drivers/video/i810/i810_main.c
+++ b/drivers/video/i810/i810_main.c
@@ -1554,15 +1554,17 @@ static struct fb_ops i810fb_ops __devinitdata = {
/***********************************************************************
* Power Management *
***********************************************************************/
-static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
+static int i810fb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct i810fb_par *par = info->par;
- par->cur_state = state.event;
+ par->cur_state = mesg.event;
- if (state.event == PM_EVENT_FREEZE) {
- dev->dev.power.power_state = state;
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_PRETHAW:
+ dev->dev.power.power_state = mesg;
return 0;
}
@@ -1578,7 +1580,7 @@ static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
pci_save_state(dev);
pci_disable_device(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
+ pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem();
return 0;
diff --git a/drivers/video/intelfb/Makefile b/drivers/video/intelfb/Makefile
index 722d21d6e5c..6c782d3ae1b 100644
--- a/drivers/video/intelfb/Makefile
+++ b/drivers/video/intelfb/Makefile
@@ -1,6 +1,8 @@
obj-$(CONFIG_FB_INTEL) += intelfb.o
-intelfb-objs := intelfbdrv.o intelfbhw.o
+intelfb-y := intelfbdrv.o intelfbhw.o
+intelfb-$(CONFIG_FB_INTEL_I2C) += intelfb_i2c.o
+intelfb-objs := $(intelfb-y)
ifdef CONFIG_FB_INTEL_DEBUG
#EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP
diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h
index e290d7460e1..80b94c19a9f 100644
--- a/drivers/video/intelfb/intelfb.h
+++ b/drivers/video/intelfb/intelfb.h
@@ -6,6 +6,10 @@
#include <linux/agp_backend.h>
#include <linux/fb.h>
+#ifdef CONFIG_FB_INTEL_I2C
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#endif
/*** Version/name ***/
#define INTELFB_VERSION "0.9.4"
@@ -115,6 +119,29 @@
/* Intel agpgart driver */
#define AGP_PHYSICAL_MEMORY 2
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+
+/* these are outputs from the chip - integrated only
+ external chips are via DVO or SDVO output */
+#define INTELFB_OUTPUT_UNUSED 0
+#define INTELFB_OUTPUT_ANALOG 1
+#define INTELFB_OUTPUT_DVO 2
+#define INTELFB_OUTPUT_SDVO 3
+#define INTELFB_OUTPUT_LVDS 4
+#define INTELFB_OUTPUT_TVOUT 5
+
+#define INTELFB_DVO_CHIP_NONE 0
+#define INTELFB_DVO_CHIP_LVDS 1
+#define INTELFB_DVO_CHIP_TMDS 2
+#define INTELFB_DVO_CHIP_TVOUT 4
+
+#define INTELFB_OUTPUT_PIPE_NC 0
+#define INTELFB_OUTPUT_PIPE_A 1
+#define INTELFB_OUTPUT_PIPE_B 2
+
/*** Data Types ***/
/* supported chipsets */
@@ -195,6 +222,10 @@ struct intelfb_hwstate {
u32 mem_mode;
u32 fw_blc_0;
u32 fw_blc_1;
+ u16 hwstam;
+ u16 ier;
+ u16 iir;
+ u16 imr;
};
struct intelfb_heap_data {
@@ -204,6 +235,33 @@ struct intelfb_heap_data {
u32 size; // in bytes
};
+#ifdef CONFIG_FB_INTEL_I2C
+struct intelfb_i2c_chan {
+ struct intelfb_info *dinfo;
+ u32 reg;
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+};
+#endif
+
+struct intelfb_output_rec {
+ int type;
+ int pipe;
+ int flags;
+
+#ifdef CONFIG_FB_INTEL_I2C
+ struct intelfb_i2c_chan i2c_bus;
+ struct intelfb_i2c_chan ddc_bus;
+#endif
+};
+
+struct intelfb_vsync {
+ wait_queue_head_t wait;
+ unsigned int count;
+ int pan_display;
+ u32 pan_offset;
+};
+
struct intelfb_info {
struct fb_info *info;
struct fb_ops *fbops;
@@ -220,7 +278,7 @@ struct intelfb_info {
u8 fbmem_gart;
/* mtrr support */
- u32 mtrr_reg;
+ int mtrr_reg;
u32 has_mtrr;
/* heap data */
@@ -267,6 +325,12 @@ struct intelfb_info {
int fixed_mode;
int ring_active;
int flag;
+ unsigned long irq_flags;
+ int open;
+
+ /* vsync */
+ struct intelfb_vsync vsync;
+ spinlock_t int_lock;
/* hw cursor */
int cursor_on;
@@ -285,12 +349,25 @@ struct intelfb_info {
/* index into plls */
int pll_index;
+
+ /* outputs */
+ int num_outputs;
+ struct intelfb_output_rec output[MAX_OUTPUTS];
};
#define IS_I9XX(dinfo) (((dinfo)->chipset == INTEL_915G)||(dinfo->chipset == INTEL_915GM)||((dinfo)->chipset == INTEL_945G)||(dinfo->chipset==INTEL_945GM))
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
/*** function prototypes ***/
extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var);
+#ifdef CONFIG_FB_INTEL_I2C
+extern void intelfb_create_i2c_busses(struct intelfb_info *dinfo);
+extern void intelfb_delete_i2c_busses(struct intelfb_info *dinfo);
+#endif
+
#endif /* _INTELFB_H */
diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c
new file mode 100644
index 00000000000..c1113d6e941
--- /dev/null
+++ b/drivers/video/intelfb/intelfb_i2c.c
@@ -0,0 +1,200 @@
+/**************************************************************************
+
+ Copyright 2006 Dave Airlie <airlied@linux.ie>
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+on the rights to use, copy, modify, merge, publish, distribute, sub
+license, and/or sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/io.h>
+
+#include "intelfb.h"
+#include "intelfbhw.h"
+
+/* bit locations in the registers */
+#define SCL_DIR_MASK 0x0001
+#define SCL_DIR 0x0002
+#define SCL_VAL_MASK 0x0004
+#define SCL_VAL_OUT 0x0008
+#define SCL_VAL_IN 0x0010
+#define SDA_DIR_MASK 0x0100
+#define SDA_DIR 0x0200
+#define SDA_VAL_MASK 0x0400
+#define SDA_VAL_OUT 0x0800
+#define SDA_VAL_IN 0x1000
+
+static void intelfb_gpio_setscl(void *data, int state)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK);
+ val = INREG(chan->reg);
+}
+
+static void intelfb_gpio_setsda(void *data, int state)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK);
+ val = INREG(chan->reg);
+}
+
+static int intelfb_gpio_getscl(void *data)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, SCL_DIR_MASK);
+ OUTREG(chan->reg, 0);
+ val = INREG(chan->reg);
+ return ((val & SCL_VAL_IN) != 0);
+}
+
+static int intelfb_gpio_getsda(void *data)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, SDA_DIR_MASK);
+ OUTREG(chan->reg, 0);
+ val = INREG(chan->reg);
+ return ((val & SDA_VAL_IN) != 0);
+}
+
+static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo,
+ struct intelfb_i2c_chan *chan,
+ const u32 reg, const char *name)
+{
+ int rc;
+
+ chan->dinfo = dinfo;
+ chan->reg = reg;
+ snprintf(chan->adapter.name, I2C_NAME_SIZE, "intelfb %s", name);
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.id = I2C_HW_B_INTELFB;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &chan->dinfo->pdev->dev;
+ chan->algo.setsda = intelfb_gpio_setsda;
+ chan->algo.setscl = intelfb_gpio_setscl;
+ chan->algo.getsda = intelfb_gpio_getsda;
+ chan->algo.getscl = intelfb_gpio_getscl;
+ chan->algo.udelay = 40;
+ chan->algo.timeout = 20;
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ /* Raise SCL and SDA */
+ intelfb_gpio_setsda(chan, 1);
+ intelfb_gpio_setscl(chan, 1);
+ udelay(20);
+
+ rc = i2c_bit_add_bus(&chan->adapter);
+ if (rc == 0)
+ DBG_MSG("I2C bus %s registered.\n", name);
+ else
+ WRN_MSG("Failed to register I2C bus %s.\n", name);
+ return rc;
+}
+
+void intelfb_create_i2c_busses(struct intelfb_info *dinfo)
+{
+ int i = 0;
+
+ /* everyone has at least a single analog output */
+ dinfo->num_outputs = 1;
+ dinfo->output[i].type = INTELFB_OUTPUT_ANALOG;
+
+ /* setup the DDC bus for analog output */
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, "CRTDDC_A");
+ i++;
+
+ /* need to add the output busses for each device
+ - this function is very incomplete
+ - i915GM has LVDS and TVOUT for example
+ */
+ switch(dinfo->chipset) {
+ case INTEL_830M:
+ case INTEL_845G:
+ case INTEL_855GM:
+ case INTEL_865G:
+ dinfo->output[i].type = INTELFB_OUTPUT_DVO;
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOD, "DVODDC_D");
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "DVOI2C_E");
+ i++;
+ break;
+ case INTEL_915G:
+ case INTEL_915GM:
+ /* has some LVDS + tv-out */
+ case INTEL_945G:
+ case INTEL_945GM:
+ /* SDVO ports have a single control bus - 2 devices */
+ dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "SDVOCTRL_E");
+ /* TODO: initialize the SDVO */
+// I830SDVOInit(pScrn, i, DVOB);
+ i++;
+
+ /* set up SDVOC */
+ dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
+ dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus;
+ /* TODO: initialize the SDVO */
+// I830SDVOInit(pScrn, i, DVOC);
+ i++;
+ break;
+ }
+ dinfo->num_outputs = i;
+}
+
+void intelfb_delete_i2c_busses(struct intelfb_info *dinfo)
+{
+ int i;
+
+ for (i = 0; i < MAX_OUTPUTS; i++) {
+ if (dinfo->output[i].i2c_bus.dinfo) {
+ i2c_bit_del_bus(&dinfo->output[i].i2c_bus.adapter);
+ dinfo->output[i].i2c_bus.dinfo = NULL;
+ }
+ if (dinfo->output[i].ddc_bus.dinfo) {
+ i2c_bit_del_bus(&dinfo->output[i].ddc_bus.adapter);
+ dinfo->output[i].ddc_bus.dinfo = NULL;
+ }
+ }
+}
diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c
index 06af89d44a0..6f9de04193d 100644
--- a/drivers/video/intelfb/intelfbdrv.c
+++ b/drivers/video/intelfb/intelfbdrv.c
@@ -136,6 +136,8 @@
static void __devinit get_initial_mode(struct intelfb_info *dinfo);
static void update_dinfo(struct intelfb_info *dinfo,
struct fb_var_screeninfo *var);
+static int intelfb_open(struct fb_info *info, int user);
+static int intelfb_release(struct fb_info *info, int user);
static int intelfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info);
static int intelfb_set_par(struct fb_info *info);
@@ -194,6 +196,8 @@ static int num_registered = 0;
/* fb ops */
static struct fb_ops intel_fb_ops = {
.owner = THIS_MODULE,
+ .fb_open = intelfb_open,
+ .fb_release = intelfb_release,
.fb_check_var = intelfb_check_var,
.fb_set_par = intelfb_set_par,
.fb_setcolreg = intelfb_setcolreg,
@@ -446,6 +450,8 @@ cleanup(struct intelfb_info *dinfo)
if (!dinfo)
return;
+ intelfbhw_disable_irq(dinfo);
+
fb_dealloc_cmap(&dinfo->info->cmap);
kfree(dinfo->info->pixmap.addr);
@@ -467,6 +473,11 @@ cleanup(struct intelfb_info *dinfo)
agp_free_memory(dinfo->gtt_ring_mem);
}
+#ifdef CONFIG_FB_INTEL_I2C
+ /* un-register I2C bus */
+ intelfb_delete_i2c_busses(dinfo);
+#endif
+
if (dinfo->mmio_base)
iounmap((void __iomem *)dinfo->mmio_base);
if (dinfo->aperture.virtual)
@@ -844,6 +855,11 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
if (bailearly == 5)
bailout(dinfo);
+#ifdef CONFIG_FB_INTEL_I2C
+ /* register I2C bus */
+ intelfb_create_i2c_busses(dinfo);
+#endif
+
if (bailearly == 6)
bailout(dinfo);
@@ -888,6 +904,13 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
}
dinfo->registered = 1;
+ dinfo->open = 0;
+
+ init_waitqueue_head(&dinfo->vsync.wait);
+ spin_lock_init(&dinfo->int_lock);
+ dinfo->irq_flags = 0;
+ dinfo->vsync.pan_display = 0;
+ dinfo->vsync.pan_offset = 0;
return 0;
@@ -1188,6 +1211,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
***************************************************************/
static int
+intelfb_open(struct fb_info *info, int user)
+{
+ struct intelfb_info *dinfo = GET_DINFO(info);
+
+ if (user) {
+ dinfo->open++;
+ }
+
+ return 0;
+}
+
+static int
+intelfb_release(struct fb_info *info, int user)
+{
+ struct intelfb_info *dinfo = GET_DINFO(info);
+
+ if (user) {
+ dinfo->open--;
+ msleep(1);
+ if (!dinfo->open) {
+ intelfbhw_disable_irq(dinfo);
+ }
+ }
+
+ return 0;
+}
+
+static int
intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
int change_var = 0;
@@ -1433,6 +1484,19 @@ static int
intelfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
{
int retval = 0;
+ struct intelfb_info *dinfo = GET_DINFO(info);
+ u32 pipe = 0;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ if (get_user(pipe, (__u32 __user *)arg))
+ return -EFAULT;
+
+ retval = intelfbhw_wait_for_vsync(dinfo, pipe);
+ break;
+ default:
+ break;
+ }
return retval;
}
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c
index 2a9322f9cfd..f887f1efd3f 100644
--- a/drivers/video/intelfb/intelfbhw.c
+++ b/drivers/video/intelfb/intelfbhw.c
@@ -32,6 +32,7 @@
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
@@ -368,7 +369,13 @@ intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
offset += dinfo->fb.offset << 12;
- OUTREG(DSPABASE, offset);
+ dinfo->vsync.pan_offset = offset;
+ if ((var->activate & FB_ACTIVATE_VBL) && !intelfbhw_enable_irq(dinfo, 0)) {
+ dinfo->vsync.pan_display = 1;
+ } else {
+ dinfo->vsync.pan_display = 0;
+ OUTREG(DSPABASE, offset);
+ }
return 0;
}
@@ -585,6 +592,11 @@ intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
hw->fw_blc_0 = INREG(FW_BLC_0);
hw->fw_blc_1 = INREG(FW_BLC_1);
+ hw->hwstam = INREG16(HWSTAM);
+ hw->ier = INREG16(IER);
+ hw->iir = INREG16(IIR);
+ hw->imr = INREG16(IMR);
+
return 0;
}
@@ -613,6 +625,7 @@ static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, int lvd
return vco / p;
}
+#if REGDUMP
static void
intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
{
@@ -638,6 +651,7 @@ intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
*o_p1 = p1;
*o_p2 = p2;
}
+#endif
void
@@ -794,6 +808,10 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw)
printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0);
printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1);
+ printk(" HWSTAM 0x%04x\n", hw->hwstam);
+ printk(" IER 0x%04x\n", hw->ier);
+ printk(" IIR 0x%04x\n", hw->iir);
+ printk(" IMR 0x%04x\n", hw->imr);
printk("hw state dump end\n");
#endif
}
@@ -1932,3 +1950,119 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) {
addr += 16;
}
}
+
+static irqreturn_t
+intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) {
+ int handled = 0;
+ u16 tmp;
+ struct intelfb_info *dinfo = (struct intelfb_info *)dev_id;
+
+ spin_lock(&dinfo->int_lock);
+
+ tmp = INREG16(IIR);
+ tmp &= VSYNC_PIPE_A_INTERRUPT;
+
+ if (tmp == 0) {
+ spin_unlock(&dinfo->int_lock);
+ return IRQ_RETVAL(handled);
+ }
+
+ OUTREG16(IIR, tmp);
+
+ if (tmp & VSYNC_PIPE_A_INTERRUPT) {
+ dinfo->vsync.count++;
+ if (dinfo->vsync.pan_display) {
+ dinfo->vsync.pan_display = 0;
+ OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+ }
+ wake_up_interruptible(&dinfo->vsync.wait);
+ handled = 1;
+ }
+
+ spin_unlock(&dinfo->int_lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+int
+intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) {
+
+ if (!test_and_set_bit(0, &dinfo->irq_flags)) {
+ if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "intelfb", dinfo)) {
+ clear_bit(0, &dinfo->irq_flags);
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&dinfo->int_lock);
+ OUTREG16(HWSTAM, 0xfffe);
+ OUTREG16(IMR, 0x0);
+ OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT);
+ spin_unlock_irq(&dinfo->int_lock);
+ } else if (reenable) {
+ u16 ier;
+
+ spin_lock_irq(&dinfo->int_lock);
+ ier = INREG16(IER);
+ if ((ier & VSYNC_PIPE_A_INTERRUPT)) {
+ DBG_MSG("someone disabled the IRQ [%08X]\n", ier);
+ OUTREG(IER, VSYNC_PIPE_A_INTERRUPT);
+ }
+ spin_unlock_irq(&dinfo->int_lock);
+ }
+ return 0;
+}
+
+void
+intelfbhw_disable_irq(struct intelfb_info *dinfo) {
+ u16 tmp;
+
+ if (test_and_clear_bit(0, &dinfo->irq_flags)) {
+ if (dinfo->vsync.pan_display) {
+ dinfo->vsync.pan_display = 0;
+ OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+ }
+ spin_lock_irq(&dinfo->int_lock);
+ OUTREG16(HWSTAM, 0xffff);
+ OUTREG16(IMR, 0xffff);
+ OUTREG16(IER, 0x0);
+
+ tmp = INREG16(IIR);
+ OUTREG16(IIR, tmp);
+ spin_unlock_irq(&dinfo->int_lock);
+
+ free_irq(dinfo->pdev->irq, dinfo);
+ }
+}
+
+int
+intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) {
+ struct intelfb_vsync *vsync;
+ unsigned int count;
+ int ret;
+
+ switch (pipe) {
+ case 0:
+ vsync = &dinfo->vsync;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ret = intelfbhw_enable_irq(dinfo, 0);
+ if (ret) {
+ return ret;
+ }
+
+ count = vsync->count;
+ ret = wait_event_interruptible_timeout(vsync->wait, count != vsync->count, HZ/10);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret == 0) {
+ intelfbhw_enable_irq(dinfo, 1);
+ DBG_MSG("wait_for_vsync timed out!\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h
index 10acda098b7..8c54ba8fbdd 100644
--- a/drivers/video/intelfb/intelfbhw.h
+++ b/drivers/video/intelfb/intelfbhw.h
@@ -88,6 +88,19 @@
#define INSTDONE 0x2090
#define PRI_RING_EMPTY 1
+#define HWSTAM 0x2098
+#define IER 0x20A0
+#define IIR 0x20A4
+#define IMR 0x20A8
+#define VSYNC_PIPE_A_INTERRUPT (1 << 7)
+#define PIPE_A_EVENT_INTERRUPT (1 << 4)
+#define VSYNC_PIPE_B_INTERRUPT (1 << 5)
+#define PIPE_B_EVENT_INTERRUPT (1 << 4)
+#define HOST_PORT_EVENT_INTERRUPT (1 << 3)
+#define CAPTURE_EVENT_INTERRUPT (1 << 2)
+#define USER_DEFINED_INTERRUPT (1 << 1)
+#define BREAKPOINT_INTERRUPT 1
+
#define INSTPM 0x20c0
#define SYNC_FLUSH_ENABLE (1 << 5)
@@ -113,6 +126,12 @@
#define FW_DISPC_BL_SHIFT 8
#define FW_DISPC_BL_MASK 0x7
+#define GPIOA 0x5010
+#define GPIOB 0x5014
+#define GPIOC 0x5018 // this may be external DDC on i830
+#define GPIOD 0x501C // this is DVO DDC
+#define GPIOE 0x5020 // this is DVO i2C
+#define GPIOF 0x5024
/* PLL registers */
#define VGA0_DIVISOR 0x06000
@@ -468,9 +487,12 @@
/* I/O macros */
#define INREG8(addr) readb((u8 __iomem *)(dinfo->mmio_base + (addr)))
+#define INREG16(addr) readw((u16 __iomem *)(dinfo->mmio_base + (addr)))
#define INREG(addr) readl((u32 __iomem *)(dinfo->mmio_base + (addr)))
#define OUTREG8(addr, val) writeb((val),(u8 __iomem *)(dinfo->mmio_base + \
(addr)))
+#define OUTREG16(addr, val) writew((val),(u16 __iomem *)(dinfo->mmio_base + \
+ (addr)))
#define OUTREG(addr, val) writel((val),(u32 __iomem *)(dinfo->mmio_base + \
(addr)))
@@ -545,5 +567,8 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg,
extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width,
int height, u8 *data);
extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo);
+extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable);
+extern void intelfbhw_disable_irq(struct intelfb_info *dinfo);
+extern int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe);
#endif /* _INTELFBHW_H */
diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c
index 57abbae5520..795c1a99a68 100644
--- a/drivers/video/matrox/i2c-matroxfb.c
+++ b/drivers/video/matrox/i2c-matroxfb.c
@@ -95,12 +95,12 @@ static struct i2c_adapter matrox_i2c_adapter_template =
static struct i2c_algo_bit_data matrox_i2c_algo_template =
{
- NULL,
- matroxfb_gpio_setsda,
- matroxfb_gpio_setscl,
- matroxfb_gpio_getsda,
- matroxfb_gpio_getscl,
- 10, 10, 100,
+ .setsda = matroxfb_gpio_setsda,
+ .setscl = matroxfb_gpio_setscl,
+ .getsda = matroxfb_gpio_getsda,
+ .getscl = matroxfb_gpio_getscl,
+ .udelay = 10,
+ .timeout = 100,
};
static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo,
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c
index d4f85011787..f8cd4c519ae 100644
--- a/drivers/video/nvidia/nvidia.c
+++ b/drivers/video/nvidia/nvidia.c
@@ -950,24 +950,25 @@ static struct fb_ops nvidia_fb_ops = {
};
#ifdef CONFIG_PM
-static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state)
+static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct nvidia_par *par = info->par;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
acquire_console_sem();
- par->pm_state = state.event;
+ par->pm_state = mesg.event;
- if (state.event == PM_EVENT_FREEZE) {
- dev->dev.power.power_state = state;
- } else {
+ if (mesg.event == PM_EVENT_SUSPEND) {
fb_set_suspend(info, 1);
nvidiafb_blank(FB_BLANK_POWERDOWN, info);
nvidia_write_regs(par, &par->SavedReg);
pci_save_state(dev);
pci_disable_device(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
+ pci_set_power_state(dev, pci_choose_state(dev, mesg));
}
+ dev->dev.power.power_state = mesg;
release_console_sem();
return 0;
diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c
index 940ba2be55e..78dc59a1751 100644
--- a/drivers/video/pvr2fb.c
+++ b/drivers/video/pvr2fb.c
@@ -187,7 +187,7 @@ static short do_blank = 0; /* (Un)Blank the screen */
static unsigned int is_blanked = 0; /* Is the screen blanked? */
#ifdef CONFIG_SH_STORE_QUEUES
-static struct sq_mapping *pvr2fb_map;
+static unsigned long pvr2fb_map;
#endif
#ifdef CONFIG_SH_DMA
@@ -213,15 +213,17 @@ static irqreturn_t pvr2fb_interrupt(int irq, void *dev_id, struct pt_regs *fp);
static int pvr2_init_cable(void);
static int pvr2_get_param(const struct pvr2_params *p, const char *s,
int val, int size);
+#ifdef CONFIG_SH_DMA
static ssize_t pvr2fb_write(struct file *file, const char *buf,
size_t count, loff_t *ppos);
+#endif
static struct fb_ops pvr2fb_ops = {
- .owner = THIS_MODULE,
- .fb_setcolreg = pvr2fb_setcolreg,
- .fb_blank = pvr2fb_blank,
- .fb_check_var = pvr2fb_check_var,
- .fb_set_par = pvr2fb_set_par,
+ .owner = THIS_MODULE,
+ .fb_setcolreg = pvr2fb_setcolreg,
+ .fb_blank = pvr2fb_blank,
+ .fb_check_var = pvr2fb_check_var,
+ .fb_set_par = pvr2fb_set_par,
#ifdef CONFIG_SH_DMA
.fb_write = pvr2fb_write,
#endif
@@ -783,7 +785,7 @@ static int __init pvr2fb_common_init(void)
goto out_err;
}
- fb_memset((unsigned long)fb_info->screen_base, 0, pvr2_fix.smem_len);
+ fb_memset(fb_info->screen_base, 0, pvr2_fix.smem_len);
pvr2_fix.ypanstep = nopan ? 0 : 1;
pvr2_fix.ywrapstep = nowrap ? 0 : 1;
@@ -820,7 +822,7 @@ static int __init pvr2fb_common_init(void)
modememused >> 10, (unsigned long)(fb_info->fix.smem_len >> 10));
printk("fb%d: Mode %dx%d-%d pitch = %ld cable: %s video output: %s\n",
fb_info->node, fb_info->var.xres, fb_info->var.yres,
- fb_info->var.bits_per_pixel,
+ fb_info->var.bits_per_pixel,
get_line_length(fb_info->var.xres, fb_info->var.bits_per_pixel),
(char *)pvr2_get_param(cables, NULL, cable_type, 3),
(char *)pvr2_get_param(outputs, NULL, video_output, 3));
@@ -829,10 +831,10 @@ static int __init pvr2fb_common_init(void)
printk(KERN_NOTICE "fb%d: registering with SQ API\n", fb_info->node);
pvr2fb_map = sq_remap(fb_info->fix.smem_start, fb_info->fix.smem_len,
- fb_info->fix.id);
+ fb_info->fix.id, pgprot_val(PAGE_SHARED));
printk(KERN_NOTICE "fb%d: Mapped video memory to SQ addr 0x%lx\n",
- fb_info->node, pvr2fb_map->sq_addr);
+ fb_info->node, pvr2fb_map);
#endif
return 0;
diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c
index e83befd16d6..d7d810dbf0b 100644
--- a/drivers/video/savage/savagefb-i2c.c
+++ b/drivers/video/savage/savagefb-i2c.c
@@ -148,7 +148,6 @@ static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan,
chan->adapter.algo_data = &chan->algo;
chan->adapter.dev.parent = &chan->par->pcidev->dev;
chan->algo.udelay = 40;
- chan->algo.mdelay = 5;
chan->algo.timeout = 20;
chan->algo.data = chan;
diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c
index 461e094e7b4..82b3deaae02 100644
--- a/drivers/video/savage/savagefb_driver.c
+++ b/drivers/video/savage/savagefb_driver.c
@@ -2323,24 +2323,24 @@ static void __devexit savagefb_remove(struct pci_dev *dev)
}
}
-static int savagefb_suspend(struct pci_dev* dev, pm_message_t state)
+static int savagefb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct savagefb_par *par = info->par;
DBG("savagefb_suspend");
-
- par->pm_state = state.event;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
+ par->pm_state = mesg.event;
+ dev->dev.power.power_state = mesg;
/*
* For PM_EVENT_FREEZE, do not power down so the console
* can remain active.
*/
- if (state.event == PM_EVENT_FREEZE) {
- dev->dev.power.power_state = state;
+ if (mesg.event == PM_EVENT_FREEZE)
return 0;
- }
acquire_console_sem();
fb_set_suspend(info, 1);
@@ -2353,7 +2353,7 @@ static int savagefb_suspend(struct pci_dev* dev, pm_message_t state)
savage_disable_mmio(par);
pci_save_state(dev);
pci_disable_device(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
+ pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem();
return 0;